1# -*- coding: UTF-8 -*-
2# vim: fdm=marker
3
4__revision__ = '$Id$'
5
6# Copyright (c) 2005-2011 Vasco Nunes, Piotr Ożarowski
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published byp
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU Library General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
21
22# You may use and distribute this software under the terms of the
23# GNU General Public License, version 2 or later
24
25import logging
26import os
27import urllib.request, urllib.error, urllib.parse
28from gi.repository import Gtk
29from gi.repository import GdkPixbuf
30from sqlalchemy.exc import IntegrityError
31
32import quick_filter
33import db
34import gutils
35import initialize
36import main_treeview
37import imp
38
39log = logging.getLogger("Griffith")
40
41### widgets ###################################################
42
43
44def clear(self):
45    """clears all fields in dialog"""
46    set_details(self, {})
47    self.widgets['add']['cb_only_empty'].set_active(False)
48
49
50def add_movie(self, details={}):
51    set_details(self, details)
52
53    self.active_plugin = ''
54    self.widgets['add']['add_button'].show()
55    self.widgets['add']['add_close_button'].show()
56    self.widgets['add']['clear_button'].show()
57    self.widgets['add']['save_button'].hide()
58    self.widgets['add']['window'].set_title(_('Add a new movie'))
59    self.widgets['add']['window'].show()
60
61
62def edit_movie(self, details={}):
63    if not 'number' in details:
64        details['number'] = gutils.find_next_available(self.db)
65    self.selected_iter_edit = self.selected_iter
66    set_details(self, details)
67    self.widgets['add']['add_button'].hide()
68    self.widgets['add']['add_close_button'].hide()
69    self.widgets['add']['clear_button'].show()
70    self.widgets['add']['save_button'].show()
71    self.widgets['add']['window'].set_title(_('Edit movie'))
72    self.widgets['add']['window'].show()
73
74
75def update_movie(self):
76    session = self.db.Session()
77
78    if self._am_movie_id is not None:
79        movie = session.query(db.Movie).filter_by(movie_id=self._am_movie_id).one()
80    else:
81        movie = session.query(db.Movie).filter_by(movie_id=self._movie_id).one()
82    if movie is None: # movie was deleted in the meantime
83        return add_movie_db(self, True)
84
85    details = get_details(self)
86
87    old_poster_md5 = movie.poster_md5
88    new_poster_md5 = old_poster_md5
89    if details['image']:
90        if old_poster_md5 != details['image']: # details["image"] can contain MD5 or file path
91            new_image_path = details['image']
92            if not os.path.isfile(new_image_path):
93                new_image_path = os.path.join(self.locations['temp'], "poster_%s.jpg" % details['image'])
94            if not os.path.isfile(new_image_path):
95                log.warn("cannot read temporary file: %s", new_image_path)
96            else:
97                new_poster_md5 = gutils.md5sum(open(new_image_path, 'rb'))
98                if session.query(db.Poster).filter_by(md5sum=new_poster_md5).count() == 0:
99                    try:
100                        data = open(new_image_path, 'rb').read()
101                    except Exception as e:
102                        log.warning("cannot read poster data")
103                        old_poster_md5 = new_poster_md5
104                    else:
105                        poster = db.Poster(md5sum=new_poster_md5, data=data)
106                        del details["image"]
107                        details['poster_md5'] = new_poster_md5
108                        session.add(poster)
109                else:
110                    details['poster_md5'] = new_poster_md5
111    else:
112        details['poster_md5'] = None
113
114    update_movie_instance(movie, details, session)
115    session.add(movie)
116
117    # delete old image
118    if old_poster_md5 and old_poster_md5 != new_poster_md5:
119        import delete
120        old_poster = session.query(db.Poster).filter_by(md5sum=old_poster_md5).first()
121        if old_poster and len(old_poster.movies) == 1: # other movies are not using the same poster
122            session.delete(old_poster)
123            delete.delete_poster_from_cache(old_poster_md5, self.locations['posters'])
124
125    if commit(session):
126        main_treeview.setmovie(self, movie, self.selected_iter_edit[0], self.treemodel)
127
128        # close add window
129        self.widgets['add']['window'].hide()
130        # refresh
131        self.treeview_clicked()
132        self.update_statusbar(_('Movie information has been updated'))
133
134
135def change_rating_from_slider(self):
136    rating = int(self.widgets['add']['rating_slider'].get_value())
137    self.widgets['add']['image_rating'].show()
138    try:
139        rimage = int(str(self.config.get('rating_image')))
140    except:
141        rimage = 0
142    if rimage:
143        prefix = ''
144    else:
145        prefix = "meter"
146    rating_file = "%s/%s0%d.png" % (self.locations['images'], prefix, rating)
147    handler = self.widgets['add']['image_rating'].set_from_pixbuf(GdkPixbuf.Pixbuf.new_from_file(rating_file))
148
149
150def populate_with_results(self):
151    w = self.widgets['add']
152    m_id = None
153    if self.founded_results_id:
154        log.info("selected id: %s", self.founded_results_id)
155        m_id = self.founded_results_id
156    else:
157        self.founded_results_id = 0
158        treeselection = self.widgets['results']['treeview'].get_selection()
159        (tmp_model, tmp_iter) = treeselection.get_selected()
160        if tmp_iter is None:
161            return False
162        m_id = tmp_model.get_value(tmp_iter, 0)
163
164    self.treemodel_results.clear()
165    self.widgets['results']['window'].hide()
166
167    plugin_name = 'PluginMovie' + self.active_plugin
168    plugin = __import__(plugin_name)
169    self.movie = plugin.Plugin(m_id)
170    self.movie.locations = self.locations
171    self.movie.config = self.config
172
173    fields_to_fetch = ['o_title', 'title', 'director', 'plot', 'cast', 'country', 'genre',
174                'classification', 'studio', 'o_site', 'site', 'trailer', 'year',
175                'notes', 'runtime', 'image', 'rating', 'screenplay', 'cameraman',
176                'resolution', 'barcode']
177    # remove fields that user doesn't want to fetch: (see preferences window)
178    fields_to_fetch = [i for i in fields_to_fetch if self.config.get("s_%s" % i, True, section='add')]
179
180    if w['cb_only_empty'].get_active(): # only empty fields
181        details = get_details(self)
182        fields_to_fetch = [i for i in fields_to_fetch if details[i] is None or details[i] == 0.0]
183    self.movie.fields_to_fetch = fields_to_fetch
184
185    if not self.movie.get_movie(w['window']):
186        return None
187    self.movie.parse_movie()
188
189    if 'year' in fields_to_fetch:
190        w['year'].set_value(int(self.movie.year))
191        fields_to_fetch.pop(fields_to_fetch.index('year'))
192    if 'runtime' in fields_to_fetch:
193        w['runtime'].set_value(int(self.movie.runtime))
194        fields_to_fetch.pop(fields_to_fetch.index('runtime'))
195    if 'cast' in fields_to_fetch:
196        cast_buffer = w['cast'].get_buffer()
197        cast_buffer.set_text(gutils.convert_entities(self.movie.cast))
198        fields_to_fetch.pop(fields_to_fetch.index('cast'))
199    if 'plot' in fields_to_fetch:
200        plot_buffer = w['plot'].get_buffer()
201        plot_buffer.set_text(gutils.convert_entities(self.movie.plot))
202        fields_to_fetch.pop(fields_to_fetch.index('plot'))
203    if 'notes' in fields_to_fetch:
204        notes_buffer = w['notes'].get_buffer()
205        notes_buffer.set_text(gutils.convert_entities(self.movie.notes))
206        fields_to_fetch.pop(fields_to_fetch.index('notes'))
207    if 'rating' in fields_to_fetch:
208        if self.movie.rating:
209            w['rating_slider'].set_value(float(self.movie.rating))
210        fields_to_fetch.pop(fields_to_fetch.index('rating'))
211    if 'resolution' in fields_to_fetch:
212        w['resolution'].get_child().set_text(gutils.convert_entities(self.movie.resolution))
213        fields_to_fetch.pop(fields_to_fetch.index('resolution'))
214    # poster
215    if 'image' in fields_to_fetch:
216        w['image'].set_text('')
217        if self.movie.image:
218            image = os.path.join(self.locations['temp'], "poster_%s.jpg" % self.movie.image)
219            try:
220                handler = self.Image.set_from_file(image)
221                pixbuf = self.Image.get_pixbuf()
222                w['picture'].set_from_pixbuf(pixbuf.scale_simple(100, 140, 3))
223                w['image'].set_text(self.movie.image)
224                w['aremove_poster'].set_sensitive(True)
225            except:
226                image = gutils.get_defaultimage_fname(self)
227                handler = self.Image.set_from_file(image)
228                w['picture'].set_from_pixbuf(self.Image.get_pixbuf())
229                w['aremove_poster'].set_sensitive(False)
230        else:
231            image = gutils.get_defaultimage_fname(self)
232            handler = self.Image.set_from_file(image)
233            Pixbuf = self.Image.get_pixbuf()
234            w['picture'].set_from_pixbuf(Pixbuf)
235            w['aremove_poster'].set_sensitive(False)
236        fields_to_fetch.pop(fields_to_fetch.index('image'))
237    # other fields
238    for i in fields_to_fetch:
239        w[i].set_text(gutils.convert_entities(self.movie[i]))
240
241
242def show_websearch_results(self):
243    total = self.founded_results_id = 0
244    for g in self.search_movie.ids:
245        if (str(g) != ''):
246            total += 1
247    if total > 1:
248        self.widgets['results']['window'].show()
249        self.widgets['results']['window'].set_keep_above(True)
250        row = None
251        key = 0
252        self.treemodel_results.clear()
253        for row in self.search_movie.ids:
254            if (str(row) != ''):
255                if isinstance(self.search_movie.titles[key], str):
256                    title = self.search_movie.titles[key]
257                else:
258                    title = str(self.search_movie.titles[key]).decode(self.search_movie.encode)
259                myiter = self.treemodel_results.insert_before(None, None)
260                self.treemodel_results.set_value(myiter, 0, str(row))
261                self.treemodel_results.set_value(myiter, 1, title)
262            key += 1
263        self.widgets['results']['treeview'].show()
264    elif total == 1:
265        self.widgets['results']['treeview'].set_cursor(total-1)
266        for row in self.search_movie.ids:
267            if (str(row) != ''):
268                self.founded_results_id = str(row)
269                populate_with_results(self)
270    else:
271        gutils.error(_("No results"), self.widgets['add']['window'])
272
273
274def get_from_web(self):
275    """search the movie in web using the active plugin"""
276
277    title = self.widgets['add']['title'].get_text()
278    o_title = self.widgets['add']['o_title'].get_text()
279
280    if o_title or title:
281        option = gutils.on_combo_box_entry_changed_name(self.widgets['add']['source'])
282        self.active_plugin = option
283        plugin_name = 'PluginMovie%s' % option
284        plugin = __import__(plugin_name)
285        if self.debug_mode:
286            log.debug('reloading %s', plugin_name)
287            import sys
288            imp.reload(sys.modules[plugin_name])
289        self.search_movie = plugin.SearchPlugin()
290        self.search_movie.config = self.config
291        self.search_movie.locations = self.locations
292        if o_title:
293            self.search_movie.url = self.search_movie.original_url_search
294            if self.search_movie.remove_accents:
295                self.search_movie.title = gutils.remove_accents(o_title, 'utf-8')
296            else:
297                self.search_movie.title = o_title
298        elif title:
299            self.search_movie.url = self.search_movie.translated_url_search
300            if self.search_movie.remove_accents:
301                self.search_movie.title = gutils.remove_accents(title, 'utf-8')
302            else:
303                self.search_movie.title = title
304        # check if internet connection is available
305        try:
306            #urllib2.urlopen("http://www.google.com")
307            urllib.request.urlopen(self.search_movie.url)
308            if self.search_movie.search_movies(self.widgets['add']['window']):
309                self.search_movie.get_searches()
310            if len(self.search_movie.ids) == 1 and o_title and title:
311                self.search_movie.url = self.search_movie.translated_url_search
312                if self.search_movie.remove_accents:
313                    self.search_movie.title = gutils.remove_accents(title, 'utf-8')
314                else:
315                    self.search_movie.title = str(title, 'utf-8')
316                if self.search_movie.search_movies(self.widgets['add']['window']):
317                    self.search_movie.get_searches()
318            self.show_search_results(self.search_movie)
319        except:
320            log.exception('')
321            gutils.error(_("Connection failed."))
322    else:
323        gutils.error(_("You should fill the original title\nor the movie title."))
324
325
326def source_changed(self):
327    option = gutils.on_combo_box_entry_changed_name(self.widgets['add']['source'])
328    self.active_plugin = option
329    plugin_name = 'PluginMovie' + option
330    plugin = __import__(plugin_name)
331    self.widgets['add']['plugin_desc'].set_text(plugin.plugin_name + "\n" \
332        + plugin.plugin_description + "\n" + _('Url: ') \
333        + plugin.plugin_url + "\n" + _('Language: ') + plugin.plugin_language)
334    image = os.path.join(self.locations['images'], plugin_name + ".png")
335    # if movie plugin logo exists lets use it
336    if os.path.exists(image):
337        handler = self.widgets['add']['plugin_image'].set_from_pixbuf(GdkPixbuf.Pixbuf.new_from_file(image))
338
339
340def get_details(self): #{{{
341    w = self.widgets['add']
342
343    cast_buffer = w['cast'].get_buffer()
344    notes_buffer = w['notes'].get_buffer()
345    plot_buffer = w['plot'].get_buffer()
346
347    t_movies = {
348        'cameraman': w['cameraman'].get_text(),
349        'classification': w['classification'].get_text(),
350        'barcode': str(gutils.digits_only(w['barcode'].get_text())),
351        'color': w['color'].get_active(),
352        'cond': w['condition'].get_active(),
353        'country': w['country'].get_text(),
354        'director': w['director'].get_text(),
355        'genre': w['genre'].get_text(),
356        'image': w['image'].get_text(),
357        'layers': w['layers'].get_active(),
358        'media_num': w['discs'].get_value(),
359        'number': w['number'].get_value(),
360        'o_site': w['o_site'].get_text(),
361        'o_title': w['o_title'].get_text(),
362        'rating': w['rating_slider'].get_value(),
363        'region': w['region'].get_active(),
364        'resolution': w['resolution'].get_child().get_text().strip(),
365        'runtime': w['runtime'].get_text(),
366        'screenplay': w['screenplay'].get_text(),
367        'site': w['site'].get_text(),
368        'studio': w['studio'].get_text(),
369        'title': w['title'].get_text(),
370        'trailer': w['trailer'].get_text(),
371        'year': w['year'].get_value(),
372        'collection_id': w['collection'].get_active(),
373        'medium_id': w['media'].get_active(),
374        'volume_id': w['volume'].get_active(),
375        'vcodec_id': w['vcodec'].get_active(),
376        'cast': cast_buffer.get_text(cast_buffer.get_start_iter(), cast_buffer.get_end_iter(), False),
377        'notes': notes_buffer.get_text(notes_buffer.get_start_iter(), notes_buffer.get_end_iter(), False),
378        'plot': plot_buffer.get_text(plot_buffer.get_start_iter(), plot_buffer.get_end_iter(), False),
379        'created': None,
380        'updated': None
381    }
382    if self._am_movie_id is not None:
383        t_movies['movie_id'] = self._am_movie_id
384
385    if t_movies['collection_id'] > 0:
386        t_movies['collection_id'] = self.collection_combo_ids[t_movies['collection_id']]
387    else:
388        t_movies['collection_id'] = None
389    if t_movies['volume_id'] > 0:
390        t_movies['volume_id'] = self.volume_combo_ids[t_movies['volume_id']]
391    else:
392        t_movies['volume_id'] = None
393    if t_movies['medium_id'] > 0:
394        t_movies['medium_id'] = self.media_ids[t_movies['medium_id']]
395    else:
396        t_movies['medium_id'] = None
397    if t_movies['vcodec_id'] > 0:
398        t_movies['vcodec_id'] = self.vcodecs_ids[t_movies['vcodec_id']]
399    else:
400        t_movies['vcodec_id'] = None
401    if t_movies['barcode'] == '0':
402        t_movies['barcode'] = None
403
404    if w['seen'].get_active():
405        t_movies['seen'] = True
406    else:
407        t_movies['seen'] = False
408    if t_movies['year'] < 1900:
409        t_movies['year'] = None
410
411    def get_id(model, text):
412        for i in model:
413            if i[1] == text:
414                return i[0]
415        return None
416    # languages
417    t_movies['languages'] = set()
418     # isn't the best but it works. without it the current selection of a language field is lost
419    w['lang_treeview'].child_focus(Gtk.DirectionType.TAB_FORWARD)
420    for row in self.lang['model']:
421        lang_id = get_id(self.lang['lang'], row[0])
422        lang_type = get_id(self.lang['type'], row[1])
423        acodec = get_id(self.lang['acodec'], row[2])
424        achannel = get_id(self.lang['achannel'], row[3])
425        subformat = get_id(self.lang['subformat'], row[4])
426        t_movies['languages'].add((lang_id, lang_type, acodec, achannel, subformat))
427
428    # tags
429    t_movies['tags'] = {}
430    for i in self.tags_ids:
431        if self.am_tags[i].get_active() == True:
432            t_movies['tags'][self.tags_ids[i]] = 1
433
434    validate_details(t_movies)
435
436    return t_movies    #}}}
437
438
439def set_details(self, item=None):#{{{
440    if item is None:
441        item = {}
442    if 'movie_id' in item and item['movie_id']:
443        self._am_movie_id = item['movie_id']
444    else:
445        self._am_movie_id = None
446    w = self.widgets['add']
447
448    cast_buffer = w['cast'].get_buffer()
449    notes_buffer = w['notes'].get_buffer()
450    plot_buffer = w['plot'].get_buffer()
451
452    if 'o_title' in item and item['o_title']:
453        w['o_title'].set_text(item['o_title'])
454    else:
455        w['o_title'].set_text('')
456    if 'title' in item and item['title']:
457        w['title'].set_text(item['title'])
458    else:
459        w['title'].set_text('')
460    if 'number' in item and item['number']:
461        w['number'].set_value(int(item['number']))
462    else:
463        w['number'].set_value(int(gutils.find_next_available(self.db)))
464    if 'title' in item and item['title']:
465        w['title'].set_text(item['title'])
466    if 'year' in item and item['year']:
467        w['year'].set_value(gutils.digits_only(item['year'], 2100))
468    else:
469        w['year'].set_value(0)
470    if 'resolution' in item and item['resolution']:
471        if self.config.get('use_resolution_alias', True):
472            w['resolution'].get_child().set_text(item['resolution'])
473        elif 'height' in item and item['height'] and 'width' in item and item['width']:
474            w['resolution'].get_child().set_text("%dx%d" % (item['width'], item['height']))
475        else: # failback to 'resolution'
476            w['resolution'].get_child().set_text(item['resolution'])
477    else:
478        w['resolution'].get_child().set_text('')
479    if 'runtime' in item and item['runtime']:
480        w['runtime'].set_value(gutils.digits_only(item['runtime']))
481    else:
482        w['runtime'].set_value(0)
483    if 'barcode' in item and item['barcode']:
484        w['barcode'].set_text(item['barcode'])
485    else:
486        w['barcode'].set_text('')
487    if 'cameraman' in item and item['cameraman']:
488        w['cameraman'].set_text(item['cameraman'])
489    else:
490        w['cameraman'].set_text('')
491    if 'screenplay' in item and item['screenplay']:
492        w['screenplay'].set_text(item['screenplay'])
493    else:
494        w['screenplay'].set_text('')
495    if 'country' in item and item['country']:
496        w['country'].set_text(item['country'])
497    else:
498        w['country'].set_text('')
499    if 'classification' in item and item['classification']:
500        w['classification'].set_text(item['classification'])
501    else:
502        w['classification'].set_text('')
503    if 'studio' in item and item['studio']:
504        w['studio'].set_text(item['studio'])
505    else:
506        w['studio'].set_text('')
507    if 'o_site' in item and item['o_site']:
508        w['o_site'].set_text(item['o_site'])
509    else:
510        w['o_site'].set_text('')
511    if 'director' in item and item['director']:
512        w['director'].set_text(item['director'])
513    else:
514        w['director'].set_text('')
515    if 'site' in item and item['site']:
516        w['site'].set_text(item['site'])
517    else:
518        w['site'].set_text('')
519    if 'trailer' in item and item['trailer']:
520        w['trailer'].set_text(item['trailer'])
521    else:
522        w['trailer'].set_text('')
523    if 'genre' in item and item['genre']:
524        w['genre'].set_text(item['genre'])
525    else:
526        w['genre'].set_text('')
527    if 'color' in item and item['color']:
528        w['color'].set_active(gutils.digits_only(item['color'], 3))
529    else:
530        w['color'].set_active(gutils.digits_only(self.config.get('color', 0, section='defaults'), 3))
531    if 'layers' in item and item['layers']:
532        w['layers'].set_active(gutils.digits_only(item['layers'], 4))
533    else:
534        w['layers'].set_active(gutils.digits_only(self.config.get('layers', 0, section='defaults'), 4))
535    if 'region' in item and item['region'] >= 0:
536        w['region'].set_active(gutils.digits_only(item['region'], 11))
537    else:
538        w['region'].set_active(gutils.digits_only(self.config.get('region', 0, section='defaults'), 11))
539    if 'cond' in item and item['cond'] is not None:
540        w['condition'].set_active(gutils.digits_only(item['cond'], 5))
541    else:
542        w['condition'].set_active(gutils.digits_only(self.config.get('condition', 0, section='defaults'), 5))
543    if 'media_num' in item and item['media_num']:
544        w['discs'].set_value(gutils.digits_only(item['media_num']))
545    else:
546        w['discs'].set_value(1)
547    if 'rating' in item and item['rating']:
548        w['rating_slider'].set_value(gutils.digits_only(item['rating'], 10))
549    else:
550        w['rating_slider'].set_value(0)
551    if 'seen' in item:
552        if item['seen'] is True:
553            w['seen'].set_active(True)
554        else:
555            w['seen'].set_active(False)
556    else:
557        w['seen'].set_active(bool(self.config.get('seen', True, section='defaults')))
558    if 'cast' in item and item['cast']:
559        cast_buffer.set_text(item['cast'])
560    else:
561        cast_buffer.set_text('')
562    if 'notes' in item and item['notes']:
563        notes_buffer.set_text(item['notes'])
564    else:
565        notes_buffer.set_text('')
566    if 'plot' in item and item['plot']:
567        plot_buffer.set_text(item['plot'])
568    else:
569        plot_buffer.set_text('')
570    pos = 0
571    if 'medium_id' in item and item['medium_id']:
572        pos = gutils.findKey(item['medium_id'], self.media_ids)
573    else:
574        pos = gutils.findKey(int(self.config.get('media', 0, section='defaults')), self.media_ids)
575    if pos is not None:
576        w['media'].set_active(int(pos))
577    else:
578        w['media'].set_active(0)
579    pos = 0
580    if 'vcodec_id' in item and item['vcodec_id']:
581        pos = gutils.findKey(item['vcodec_id'], self.vcodecs_ids)
582    else:
583        pos = gutils.findKey(int(self.config.get('vcodec', 0, section='defaults')), self.vcodecs_ids)
584    if pos is not None:
585        w['vcodec'].set_active(int(pos))
586    else:
587        w['vcodec'].set_active(0)
588    pos = 0
589    if 'volume_id' in item and item['volume_id']:
590        pos = gutils.findKey(item['volume_id'], self.volume_combo_ids)
591    if pos is not None:
592        w['volume'].set_active(int(pos))
593    else:
594        w['volume'].set_active(0)
595    pos = 0
596    if 'collection_id' in item and item['collection_id']:
597        pos = gutils.findKey(item['collection_id'], self.collection_combo_ids)
598    if pos is not None:
599        w['collection'].set_active(int(pos))
600    else:
601        w['collection'].set_active(0)
602    # tags
603    for tag in self.am_tags:
604        self.am_tags[tag].set_active(False)
605    if 'tags' in item:
606        for tag in item['tags']:
607            i = gutils.findKey(tag.tag_id, self.tags_ids)
608            self.am_tags[i].set_active(True)
609    # languages
610    w['lang_treeview'].get_model().clear()
611    if 'languages' in item and len(item['languages']) > 0:
612        for i in item['languages']:
613            self.create_language_row(i)
614    # poster
615    w['aremove_poster'].set_sensitive(True)
616    if 'poster_md5' in item and item['poster_md5']:
617        image_path = gutils.get_image_fname(item["poster_md5"], self.db, 'm')
618        if not image_path:
619            image_path = '' # isfile doesn't like bool
620            w['aremove_poster'].set_sensitive(False)
621        w['image'].set_text(item['poster_md5'])
622    elif 'image' in item and item['image']:
623        if len(item['image']) == 32: # md5
624            image_path = gutils.get_image_fname(item["image"], self.db, 'm')
625            if not image_path:
626                image_path = '' # isfile doesn't like bool
627                w['aremove_poster'].set_sensitive(False)
628            else:
629                w['image'].set_text(item['image'])
630        else:
631            image_path = os.path.join(self.locations['posters'], "m_%s.jpg" % item['image'])
632            log.warn("TODO: image=%s", item['image'])
633    else:
634        w['image'].set_text('')
635        image_path = gutils.get_defaultimage_fname(self)
636        w['aremove_poster'].set_sensitive(False)
637    if not os.path.isfile(image_path):
638        image_path = gutils.get_defaultimage_fname(self)
639        w['aremove_poster'].set_sensitive(False)
640    w['picture'].set_from_file(image_path)
641
642    w['notebook'].set_current_page(0)
643    w['o_title'].grab_focus()
644    #}}}
645
646
647def validate_details(t_movies, allow_only=None):
648    for i in list(t_movies.keys()):
649        if t_movies[i] == '':
650            t_movies[i] = None
651    for i in ('color', 'cond', 'layers', 'media', 'vcodec'):
652        if i in t_movies and t_movies[i] < 1:
653            t_movies[i] = None
654    for i in ('volume_id', 'collection_id', 'runtime'):
655        if i in t_movies and (t_movies[i] is None or int(t_movies[i]) == 0):
656            t_movies[i] = None
657    if allow_only is not None:
658        # iterate over a copy of keys of the dict because removing elements of a dict
659        # within a for enumeration of the same dict instance isn't supported
660        for i in list(t_movies.keys()):
661            if not i in allow_only:
662                t_movies.pop(i)
663
664
665### database part #############################################
666
667
668def add_movie_db(self, close):
669    session = self.db.Session()
670    details = get_details(self)
671
672    if not details['o_title'] and not details['title']:
673        gutils.error(_("You should fill the original title\nor the movie title."),
674            parent=self.widgets['add']['window'])
675        return False
676
677    asked = False
678    if details['o_title']:
679        if session.query(db.Movie).filter_by(o_title=details['o_title']).count() > 0:
680            asked = True
681            if not gutils.question(_('Movie with that title already exists, are you sure you want to add?'), self.widgets['add']['window']):
682                return False
683    if not asked and details['title']:
684        if session.query(db.Movie).filter_by(title=details['title']).count() > 0:
685            if not gutils.question(_('Movie with that title already exists, are you sure you want to add?'), self.widgets['add']['window']):
686                return False
687
688    new_poster_md5 = None
689    if details['image']:
690        tmp_image_path = original_image_path = details['image']
691        if not os.path.isfile(tmp_image_path):
692            tmp_image_path = os.path.join(self.locations['temp'], "poster_%s.jpg" % details['image'])
693        if os.path.isfile(tmp_image_path):
694            file_object = open(tmp_image_path, 'rb')
695            try:
696                new_poster_md5 = gutils.md5sum(file_object)
697
698                if session.query(db.Poster).filter_by(md5sum=new_poster_md5).count() == 0:
699                    try:
700                        file_object.seek(0, 0);
701                        data = file_object.read()
702                    except Exception as e:
703                        log.warning("cannot read poster data")
704                    else:
705                        poster = db.Poster(md5sum=new_poster_md5, data=data)
706                        del details["image"]
707                        details["poster_md5"] = new_poster_md5
708                        session.add(poster)
709                else:
710                    details["poster_md5"] = new_poster_md5
711            finally:
712                file_object.close()
713            try:
714                if not tmp_image_path == original_image_path:
715                    os.remove(tmp_image_path)
716            except Exception as e:
717                log.warn("cannot remove temporary file %s", tmp_image_path)
718        else:
719            log.warn("cannot read temporary file: %s", tmp_image_path)
720
721
722    movie = update_movie_instance(None, details, session)
723    session.add(movie)
724    if not commit(session):
725        return False
726
727    # create new entry, unselect current movie and select new entry in main treelist
728    myiter = main_treeview.addmovie(self, movie)
729    main_treeview.select(self, None)
730    main_treeview.select(self, myiter)
731
732    # update statusbar
733    self.total += 1
734    self.count_statusbar()
735
736    clear(self)
737
738    if close:
739        self.hide_add_window()
740
741
742def clone_movie(self):
743    session = self.db.Session()
744
745    if self.selected_iter[0] is None:
746        log.warn("cannot clone movie: no item selected")
747        return False
748    movie = session.query(db.Movie).filter_by(number=self.selected[0]).first()
749
750    if movie is None:
751        log.warn("cannot clone movie: Movie(%s) not found", number)
752        return False
753
754    next_number = gutils.find_next_available(self.db)
755
756    # integer problem workaround
757    if int(movie.seen) == 1:
758        seen = True
759    else:
760        seen = False
761    new_movie = db.Movie()
762
763    # TODO: WARNING: loan problems (don't copy volume/collection data until resolved)
764    new_movie.cast = movie.cast
765    new_movie.classification = movie.classification
766    new_movie.vcodec_id = movie.vcodec_id
767    new_movie.barcode = movie.barcode
768    new_movie.cameraman = movie.cameraman
769    new_movie.collection_id = movie.collection_id
770    new_movie.volume_id = movie.volume_id
771    new_movie.color = movie.color
772    new_movie.cond = movie.cond
773    new_movie.country = movie.country
774    new_movie.director = movie.director
775    new_movie.genre = movie.genre
776    new_movie.site = movie.site
777    new_movie.loaned = movie.loaned
778    new_movie.layers = movie.layers
779    new_movie.medium_id = movie.medium_id
780    new_movie.number = next_number
781    new_movie.media_num = movie.media_num
782    new_movie.notes = movie.notes
783    new_movie.o_title = movie.o_title
784    new_movie.plot = movie.plot
785    new_movie.poster_md5 = movie.poster_md5
786    new_movie.ratio_id = movie.ratio_id
787    new_movie.rating = movie.rating
788    new_movie.region = movie.region
789    new_movie.runtime = movie.runtime
790    new_movie.resolution = movie.resolution
791    new_movie.screenplay = movie.screenplay
792    new_movie.seen = seen
793    new_movie.o_site = movie.o_site
794    new_movie.studio = movie.studio
795    new_movie.title = movie.title
796    new_movie.trailer = movie.trailer
797    new_movie.year = movie.year
798
799    new_movie.tags = movie.tags
800    new_movie.languages = movie.languages
801    new_movie.loans = movie.loans
802
803    # save
804    session.add(new_movie)
805    if not commit(session):
806        return False
807
808    if movie.poster_md5:
809        image_path = gutils.get_image_fname(movie.poster_md5, self.db)
810        if not image_path or not os.path.isfile(image_path):
811            image_path = gutils.get_defaultimage_fname(self)
812        handler = self.Image.set_from_file(image_path)
813
814    # change_filter calls populate_treeview which updates the status bar
815    quick_filter.change_filter(self)
816
817
818def update_movie_instance(movie, details, session):
819    if not movie:
820        movie = db.Movie()
821    if details is not None:
822        t_tags = t_languages = None
823        if 'tags' in details:
824            t_tags = details.pop('tags')
825        if 'languages' in details:
826            t_languages = details.pop('languages')
827        #for i in db.tables.movies.columns.keys():
828        for i in details:
829            if i not in ('created', 'updated') and hasattr(movie, i):
830                setattr(movie, i, details[i])
831
832        # clear previous data (in case of updates)
833        if movie.languages:
834            movie.languages = []
835        if movie.tags:
836            movie.tags = []
837        # languages
838        if t_languages is not None:
839            for lang in t_languages:
840                if lang[0] > 0:
841                    ml = db.MovieLang(lang_id=lang[0], type=lang[1],
842                        acodec_id=lang[2], achannel_id=lang[3], subformat_id=lang[4])
843                    movie.languages.append(ml)
844        # tags
845        if t_tags is not None:
846            for tag in list(t_tags.keys()):
847                dbTag = session.query(db.Tag).filter_by(tag_id=tag).one()
848                #movie.tags.append(db.MovieTag(tag_id=tag))
849                movie.tags.append(dbTag)
850        if hasattr(movie, 'image') and movie.image: # TODO: remove it once image will be removed from movies_table
851            movie.image = None # remove MD5 or link
852    return movie
853
854
855def commit(session):
856    try:
857        session.commit()
858    except IntegrityError as e:
859        session.rollback()
860        log.warn("Cannot commit movie: %s", e.message)
861        gutils.warning(str(e.orig))
862        return False
863    except Exception as e:
864        log.error("Unexpected problem: %s", e)
865        return False
866    return True
867
868
869def add_medium(self, name):
870    session = self.db.Session()
871    medium = db.Medium(name=name)
872    session.add(medium)
873    try:
874        session.commit()
875    except Exception as e:
876        session.rollback()
877        log.warn("Cannot add medium entry: %s", e.message)
878    else:
879        initialize.media_combos(self)
880    return medium.medium_id
881
882
883def add_vcodec(self, name):
884    session = self.db.Session()
885    vcodec = db.VCodec(name=name)
886    session.add(vcodec)
887    try:
888        session.commit()
889    except Exception as e:
890        session.rollback()
891        log.warn("Cannot add video codec entry: %s", e.message)
892    else:
893        initialize.vcodec_combos(self)
894    return vcodec.vcodec_id
895
896
897def add_volume(self, name):
898    session = self.db.Session()
899    vol = db.Volume(name=name)
900    session.add(vol)
901    try:
902        session.commit()
903    except Exception as e:
904        session.rollback()
905        log.warn("Cannot add volume: %s", e.message)
906    else:
907        initialize.update_volume_combo_ids(self)
908        initialize.fill_volumes_combo(self, vol.volume_id)
909    return vol.volume_id
910
911
912def add_collection(self, name):
913    session = self.db.Session()
914    col = db.Collection(name=name)
915    session.add(col)
916    try:
917        session.commit()
918    except Exception as e:
919        session.rollback()
920        log.warn("Cannot add collection: %s", e.message)
921    else:
922        initialize.update_collection_combo_ids(self)
923        initialize.fill_collections_combo(self, col.collection_id)
924    return col.collection_id
925
926
927def change_poster(self):
928    from .edit import change_poster_select_file
929    if change_poster_select_file(self, -1, change_poster_new_movie):
930        self.widgets['add']['aremove_poster'].set_sensitive(True)
931
932
933def change_poster_new_movie(self, number, filename):
934    try:
935        handler = self.Image.set_from_file(filename)
936        pixbuf = self.Image.get_pixbuf()
937        handler = self.widgets['add']['picture'].set_from_pixbuf(pixbuf.scale_simple(100, 140, 3))
938        gutils.garbage(handler)
939        self.widgets['add']['image'].set_text(filename)
940        return True
941    except:
942        image = gutils.get_defaultimage_fname(self)
943        handler = self.Image.set_from_file(image)
944        handler = self.widgets['add']['picture'].set_from_pixbuf(self.Image.get_pixbuf())
945        gutils.garbage(handler)
946        return False
947
948
949def delete_poster(self):
950    w = self.widgets['add']
951    w['image'].set_text('')
952    image_path = gutils.get_defaultimage_fname(self)
953    w['picture'].set_from_file(image_path)
954    w['aremove_poster'].set_sensitive(False)
955