1# -*- coding: UTF-8 -*-
2
3__revision__ = '$Id$'
4
5# Copyright (c) 2006-2011
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
24import gutils
25import movie
26import string
27import re
28
29plugin_name         = "Kino.de"
30plugin_description  = "KINO.DE"
31plugin_url          = "www.kino.de"
32plugin_language     = _("German")
33plugin_author       = "Michael Jahn"
34plugin_author_email = "<mikej06@hotmail.com>"
35plugin_version      = "1.16"
36
37class Plugin(movie.Movie):
38    url_to_use_base = 'http://www.kino.de/'
39    url_to_use      = url_to_use_base + 'kinofilm/'
40    url_type        = 'K'
41
42    def __init__(self, id):
43        self.encode='iso-8859-1'
44        elements = string.split(id, "_")
45        self.movie_id = elements[1]
46        if (elements[0] == "V"):
47            self.url_to_use_base = 'http://www.video.de/'
48            self.url_to_use      = self.url_to_use_base + 'videofilm/'
49            self.url_type        = 'V'
50        else:
51            self.url_to_use_base = 'http://www.kino.de/'
52            self.url_to_use      = self.url_to_use_base + 'kinofilm/'
53            self.url_type        = 'K'
54        self.url = self.url_to_use + str(self.movie_id)
55
56    def initialize(self):
57        if self.url_type == 'K':
58            url = self.url_to_use + string.replace(str(self.movie_id), '/', '/credits/')
59            self.creditspage = self.open_page(self.parent_window, url=url)
60        else:
61            self.creditspage = ''
62        videopageforkinourl = gutils.trim(self.page, 'class="videode"', 'Zum Film auf video.de')
63        if videopageforkinourl:
64            url = gutils.trim(videopageforkinourl, 'href="', '"')
65            self.videopage = self.open_page(self.parent_window, url=url)
66        else:
67            self.videopage = None
68
69    def get_image(self):
70        self.image_url = string.replace(string.replace(gutils.trim(self.page, '"picture":', ','), '"', ''), '\\', '')
71        if not self.image_url:
72            tmpdata = gutils.regextrim(self.page, '<div class="cover-area">', '</div>')
73            if tmpdata:
74                # video page
75                tmpdata = re.search('(http[:][/][/][^/]+[/]flbilder[/][^"\']+)', tmpdata)
76                if tmpdata:
77                    self.image_url = tmpdata.group(1)
78            else:
79                # kino page
80                tmpdata = gutils.before(self.page, '<span style="line-height: 15px;">')
81                if tmpdata:
82                    tmpparts = re.split('http://images.kino.de/s/', tmpdata)
83                    if len(tmpparts) > 2:
84                        self.image_url = 'http://images.kino.de/s/' + gutils.before(tmpparts[2], '"')
85                    elif len(tmpparts) > 1:
86                        self.image_url = 'http://images.kino.de/s/' + gutils.before(tmpparts[1], '"')
87            if not self.image_url and self.videopage:
88                tmpdata = gutils.regextrim(self.videopage, '<div class="cover-area">', '</div>')
89                if tmpdata:
90                    # video page
91                    tmpdata = re.search('(http[:][/][/][^/]+[/]flbilder[/][^"\']+)', tmpdata)
92                    if tmpdata:
93                        self.image_url = tmpdata.group(1)
94
95
96    def get_o_title(self):
97        self.o_title = gutils.regextrim(self.page, '(<p>Originaltitel[:] |Originaltitel<[^>]+>)', '(</tr>|</p>)')
98        if not self.o_title:
99            self.o_title = gutils.trim(self.page, '<h1(', ')')
100            if not self.o_title:
101                self.o_title = gutils.trim(self.page, '<div class="teaser">', '</')
102                if not self.o_title:
103                    if self.videopage:
104                        self.o_title = gutils.trim(self.videopage, '<p>Originaltitel: ', '</p>')
105                    if not self.o_title:
106                        self.o_title = gutils.regextrim(self.page, '<h1>', '(</h1>|</span>)')
107
108    def get_title(self):
109        self.title = gutils.trim(self.page, '<div class="teaser">', '</')
110        if not self.title:
111            self.title = gutils.regextrim(self.page, '<h1>', '(</h1>|</span>)')
112
113    def get_director(self):
114        self.director = gutils.trim(self.page, '<th>Regie:', '<th>')
115        if not self.director:
116            self.director = gutils.trim(self.creditspage, 'Regie:', '</li>')
117
118    def get_plot(self):
119        self.plot = gutils.trim(self.page, '<div class="yui-content">', '<div class="footer">')
120        if not self.plot:
121            # kino page
122            self.plot = gutils.after(gutils.trim(self.page, 'Filmhandlung & Hintergrund', '</div>'), '</h2>')
123        if not self.plot and self.videopage:
124            self.plot = gutils.trim(self.videopage, '<div class="yui-content">', '<div class="footer">')
125        if self.plot:
126            # video page
127            self.plot = re.sub('<script type="text/javascript">[^<]+</script>', '', self.plot)
128            self.plot = string.replace(self.plot, '>Großansicht</a>', '>')
129            self.plot = string.replace(self.plot, '>Schließen</a>', '>')
130            self.plot = string.replace(self.plot, '>zur&uuml;ck </a>', '>')
131            self.plot = string.replace(self.plot, '>1</a>', '>')
132            self.plot = string.replace(self.plot, '> weiter</a>', '>')
133            self.plot = string.replace(self.plot, '</h4>', '\n')
134            self.plot = gutils.clean(self.plot)
135            compiledmultiline = re.compile(r'^[^(]+[(]Foto[:][^)]+[)][ ]*$', re.MULTILINE)
136            self.plot = compiledmultiline.sub('', self.plot)
137            compiledmultiline = re.compile(r"(^\s+$|^\s*//\s*$)", re.MULTILINE)
138            self.plot = compiledmultiline.sub('', self.plot)
139            compiledmultiline = re.compile("^[\n]+$", re.MULTILINE)
140            self.plot = compiledmultiline.sub("\n", self.plot)
141
142    def get_year(self):
143        self.year = ''
144        tmp = gutils.trim(gutils.trim(self.page, '"releaseDate":', ','), ':"', '"')
145        if tmp:
146            self.year = tmp
147        else:
148            tmp = gutils.trim(self.page, '<div class="description">', '</div>')
149            if tmp:
150                searchyearandcountry = re.search('([0-9]{4})<br', tmp)
151                if searchyearandcountry:
152                    self.year = searchyearandcountry.group(1)
153        if not self.year:
154            tmp = gutils.trim(self.page, '<span class="standardsmall"><strong>', '<br')
155            if tmp:
156                tmp = gutils.trim(tmp, '<strong>', '</strong>')
157                if tmp:
158                    srchyear = re.search('([0-9]{4})', tmp)
159                    if srchyear:
160                        self.year = srchyear.group(1)
161        if not self.year and self.videopage:
162            tmp = gutils.trim(self.videopage, '<div class="description">', '</div>')
163            if tmp:
164                searchyearandcountry = re.search('([0-9]{4})<br', tmp)
165                if searchyearandcountry:
166                    self.year = searchyearandcountry.group(1)
167
168    def get_runtime(self):
169        self.runtime = gutils.trim(self.page, '"runtime":', ',')
170        if not self.runtime:
171            srchresult = re.search('Laufzeit: ([0-9]+)[ \t]Min[.]<', self.page)
172            if srchresult != None:
173                self.runtime = srchresult.group(1)
174            if not self.runtime:
175                srchresult = re.search('>([0-9]+)[ \t]Min[.]<', self.page)
176                if srchresult != None:
177                    self.runtime = srchresult.group(1)
178            if not self.runtime and self.videopage:
179                srchresult = re.search('Laufzeit: ([0-9]+)[ \t]Min[.]<', self.videopage)
180                if srchresult != None:
181                    self.runtime = srchresult.group(1)
182
183    def get_genre(self):
184        self.genre = string.replace(gutils.trim(self.page, '"genre":', ','), '"', '')
185        if not self.genre:
186            self.genre = gutils.trim(self.page,'<p class="genre">', '</p>')
187        if not self.genre:
188            self.genre = gutils.trim(self.page, 'title="Zur Genreliste: Drama">', '<')
189        if not self.genre and self.videopage:
190            self.genre = gutils.trim(self.videopage,'<p class="genre">', '</p>')
191
192    def get_cast(self):
193        self.cast = ''
194        tmp = gutils.regextrim(self.page, '<th>Darsteller:', '(<th>[^&]|</table>)')
195        if tmp:
196            tmpparts = string.split(tmp, '<a href="/star/')
197            for tmppart in tmpparts[1:]:
198                name = gutils.trim(tmppart, '>', '<')
199                role = gutils.trim(tmppart, '>als ', '<')
200                if name:
201                    if role:
202                        self.cast = self.cast + name + _(' as ') + role + '\n'
203                    else:
204                        self.cast = self.cast + name + '\n'
205        if not self.cast:
206            elements = re.split('<h3>Darsteller</h3>', self.page)
207            for element in elements[1:]:
208                actor = gutils.trim(element, 'itemprop="name">', '<')
209                if actor:
210                    self.cast = self.cast + actor
211                    role = gutils.trim(element, 'itemprop="title">', '<')
212                    if role and role != 'Darsteller':
213                        self.cast = self.cast + _(' as ') + role
214                    self.cast = self.cast + '\n'
215
216    def get_classification(self):
217        self.classification = string.replace(gutils.trim(self.page, '"fsk":', ','), '"', '')
218        if not self.classification:
219            self.classification = gutils.regextrim(self.page, 'FSK: ', '<')
220        if not self.classification and self.videopage:
221            self.classification = gutils.regextrim(self.videopage, 'FSK: ', '<')
222
223    def get_studio(self):
224        self.studio = ''
225        tmp = gutils.trim(self.page, '<div class="description">', '</div>')
226        if tmp:
227            tmp = gutils.trim(tmp, 'Regie:', '</p>')
228            if tmp:
229                self.studio = string.replace(gutils.after(tmp, '<br/>'), 'Verleih: ', '')
230        if not self.studio:
231            self.studio = gutils.trim(self.page, 'Verleih: ', '<')
232        if not self.studio:
233            self.studio = gutils.trim(self.page, 'Verleih:', '</li>')
234        if not self.studio and self.videopage:
235            tmp = gutils.trim(self.videopage, '<div class="description">', '</div>')
236            if tmp:
237                tmp = gutils.trim(tmp, 'Regie:', '</p>')
238                if tmp:
239                    self.studio = string.replace(gutils.after(tmp, '<br/>'), 'Verleih: ', '')
240
241    def get_o_site(self):
242        self.o_site = ""
243
244    def get_site(self):
245        self.site = self.url_to_use + self.movie_id
246
247    def get_trailer(self):
248        self.trailer = ''
249        trailerparts = re.split('href="/trailer-und-bilder/film', self.page)
250        if len(trailerparts) > 1:
251            for trailerpart in trailerparts[1:]:
252                trailermatch = re.search('Trailer[ ]*</p>', trailerpart)
253                if trailermatch:
254                    self.trailer = self.url_to_use_base + 'trailer-und-bilder/film' + gutils.before(trailerpart, '"')
255                    break
256        if not self.trailer and self.url_type == 'K':
257            self.trailer = self.url_to_use + string.replace(str(self.movie_id), '/', '/trailer/')
258
259    def get_country(self):
260        self.country = ''
261        tmp = gutils.trim(self.page, '<div class="description">', '</div>')
262        if tmp:
263            searchyearandcountry = re.search('([^>0-9]+)[0-9]{4}<br', tmp)
264            if searchyearandcountry:
265                self.country = searchyearandcountry.group(1)
266        if not self.country:
267            tmp = gutils.trim(self.page, '<span class="standardsmall"><strong>', '<br')
268            if tmp:
269                tmp = gutils.trim(tmp, '<strong>', '</strong>')
270                if tmp:
271                    self.country = gutils.before(tmp, ' ')
272        if not self.country:
273            self.country = gutils.trim(self.page, 'itemprop="contentLocation">', '<')
274        if not self.country and self.videopage:
275            tmp = gutils.trim(self.videopage, '<div class="description">', '</div>')
276            if tmp:
277                searchyearandcountry = re.search('([^>0-9]+)[0-9]{4}<br', tmp)
278                if searchyearandcountry:
279                    self.country = searchyearandcountry.group(1)
280
281    def get_rating(self):
282        self.rating = 0
283        tmp = gutils.trim(self.page, '<h4>Filmbewertung</h4>', '</script>')
284        if tmp:
285            matched = re.search('ratingBar.setValue[(]([0-9]+)[)]', tmp)
286            if matched:
287                try:
288                    self.rating = round(int(matched.group(1)) / 10.0, 0)
289                except:
290                    pass
291
292    def get_notes(self):
293        self.notes = ""
294        tmp_notes = gutils.clean(gutils.trim(self.page, "<strong>Sprachen:</strong>", "</p>"))
295        if tmp_notes != "":
296            self.notes = self.notes + "Sprachen:\n" + tmp_notes + "\n\n"
297        tmp_notes = gutils.clean(gutils.trim(self.page, "<strong>Untertitel:</strong>", "</p>"))
298        if tmp_notes != "":
299            self.notes = self.notes + "Untertitel:\n" + tmp_notes + "\n\n"
300        tmp_notes = gutils.clean(gutils.trim(self.page, "<strong>Tonformat:</strong>", "</p>"))
301        if tmp_notes != "":
302            self.notes = self.notes + "Tonformat:\n" + tmp_notes + "\n\n"
303        tmp_notes = gutils.clean(gutils.trim(self.page, "<strong>Bildformat:</strong>", "</p>"))
304        if tmp_notes != "":
305            self.notes = self.notes + "Bildformat:\n" + tmp_notes + "\n\n"
306        tmp_notes = gutils.clean(gutils.trim(self.page, "<strong>EAN</strong>", "</p>"))
307        if tmp_notes != "":
308            self.notes = self.notes + "EAN:\n" + tmp_notes + "\n\n"
309
310    def get_screenplay(self):
311        self.screenplay = gutils.regextrim(self.page, '<th>Buch:', '<th>')
312        if not self.screenplay:
313            self.screenplay= gutils.trim(self.creditspage, 'Drehbuch:&nbsp;', '</tr>')
314        if not self.screenplay:
315            elements = re.split('<h3>Drehbuch</h3>', self.page)
316            delimiter = ''
317            for element in elements[1:]:
318                self.screenplay = self.screenplay + delimiter + gutils.trim(element, 'itemprop="name">', '<')
319                delimiter = ', '
320
321    def get_cameraman(self):
322        self.cameraman = gutils.regextrim(self.page, '<th>Kamera:', '(<th>|</table>)')
323        if not self.cameraman:
324            self.cameraman= gutils.trim(self.creditspage, 'Kamera&nbsp;', '</tr>')
325        if not self.cameraman:
326            elements = re.split('"function-title">Kamera</span>', self.page)
327            delimiter = ''
328            for element in elements[1:]:
329                self.cameraman = self.cameraman + delimiter + gutils.trim(element, 'itemprop="name">', '<')
330                delimiter = ', '
331
332class SearchPlugin(movie.SearchMovie):
333
334    def __init__(self):
335        self.original_url_search   = 'http://www.kino.de/suche/film?hitsPerPage=50&searchString='
336        self.translated_url_search = 'http://www.kino.de/suche/film?hitsPerPage=50&searchString='
337        self.encode='iso-8859-1'
338        self.remove_accents = False
339
340    def search(self,parent_window):
341        self.open_search(parent_window)
342        pagemovie = self.page
343        #
344        # Look for DVD and VHS
345        #
346        self.url = "http://www.kino.de/suche/video?hitsPerPage=50&searchString="
347        self.open_search(parent_window)
348        self.page = pagemovie + self.page
349
350        return self.page
351
352    def get_searches(self):
353        elements1 = re.split('href="/kinofilm/', self.page)
354        elements1[0] = None
355        for element in elements1:
356            if element != None:
357                title = gutils.clean(gutils.trim(element,'>','</a>')) + string.replace(' (' +
358                            gutils.clean(gutils.trim(element, '<p>', "<br />")) + ')', '()', '')
359                if title != ' ':
360                    self.ids.append("K_" + re.sub('[?].*', '', gutils.before(element,'"')))
361                    self.titles.append('Kino: ' + title)
362
363        elements2 = re.split('href="http://www.video.de/videofilm/', self.page)
364        elements2[0] = None
365        for element in elements2:
366            if element != None:
367                title = gutils.clean(gutils.trim(element,'>','</a>')) + string.replace(' (' +
368                            gutils.clean(gutils.trim(gutils.before(element, '</li>'), '<p>', "<br />")) + ')', '()', '')
369                if title != ' ':
370                    id = re.sub('[?].*', '', gutils.before(element,'"'))
371                    self.ids.append("V_" + id)
372                    type = ''
373                    if 'blu-ray-disc-kauf' in id:
374                        type = ' (Bluray-Kauf)'
375                    if 'blu-ray-disc-leih' in id:
376                        type = ' (Bluray-Verleih)'
377                    if 'dvd-leih' in id:
378                        type = ' (DVD-Verleih)'
379                    if 'dvd-kauf' in id:
380                        type = ' (DVD-Kauf)'
381                    self.titles.append('Video: ' + title + type)
382
383#
384# Plugin Test
385#
386class SearchPluginTest(SearchPlugin):
387    #
388    # Configuration for automated tests:
389    # dict { movie_id -> [ expected result count for original url, expected result count for translated url ] }
390    #
391    test_configuration = {
392        'Rocky Balboa'         : [ 10, 10 ],
393        'Arahan'               : [ 10, 10 ],
394        'Ein glückliches Jahr' : [ 4, 4 ]
395    }
396
397class PluginTest:
398    #
399    # Configuration for automated tests:
400    # dict { movie_id -> dict { arribute -> value } }
401    #
402    # value: * True/False if attribute only should be tested for any value
403    #        * or the expected value
404    #
405    test_configuration = {
406        'K_rocky-balboa/96132.html' : {
407            'title'               : 'Rocky Balboa',
408            'o_title'             : 'Rocky Balboa',
409            'director'            : 'Sylvester Stallone',
410            'plot'                : True,
411            'cast'                : 'Sylvester Stallone' + _(' as ') + 'Rocky Balboa\n\
412Antonio Traver' + _(' as ') + 'Mason "The Line" Dixon\n\
413Burt Young' + _(' as ') + 'Paulie\n\
414Geraldine Hughes' + _(' as ') + 'Marie\n\
415Milo Ventimiglia' + _(' as ') + 'Rocky Jr.\n\
416James Francis Kelly III' + _(' as ') + 'Steps\n\
417Tony Burton' + _(' as ') + 'Duke\n\
418A.J. Benza' + _(' as ') + 'L.C.',
419            'country'             : 'USA',
420            'genre'               : 'Drama',
421            'classification'      : '12',
422            'studio'              : 'Fox',
423            'o_site'              : False,
424            'site'                : 'http://www.kino.de/kinofilm/rocky-balboa/96132.html',
425            'trailer'             : 'http://www.kino.de/kinofilm/rocky-balboa/trailer/96132.html',
426            'year'                : 2007,
427            'notes'               : False,
428            'runtime'             : 102,
429            'image'               : True,
430            'rating'              : False,
431            'cameraman'           : 'J. Clark Mathis',
432            'screenplay'          : 'Sylvester Stallone'
433        },
434        'K_ein-glueckliches-jahr/28675.html' : {
435            'title'               : 'Ein glückliches Jahr',
436            'o_title'             : 'La bonne année',
437            'director'            : 'Claude Lelouch',
438            'plot'                : True,
439            'cast'                : 'Lino Ventura\n\
440Françoise Fabian\n\
441Charles Gérard\n\
442André Falcon',
443            'country'             : 'Frankreich/Italien',
444            'genre'               : 'Drama',
445            'classification'      : '12',
446            'studio'              : 'Columbia TriStar',
447            'o_site'              : False,
448            'site'                : 'http://www.kino.de/kinofilm/ein-glueckliches-jahr/28675.html',
449            'trailer'             : 'http://www.kino.de/kinofilm/ein-glueckliches-jahr/trailer/28675.html',
450            'year'                : 1973,
451            'notes'               : False,
452            'runtime'             : 115,
453            'image'               : True,
454            'rating'              : False,
455            'cameraman'           : 'Jean Collomb',
456            'screenplay'          : 'Claude Lelouch'
457        },
458        'V_ein-glueckliches-jahr-dvd/85546.html' : {
459            'title'               : 'Ein glückliches Jahr',
460            'o_title'             : 'La bonne année',
461            'director'            : 'Claude Lelouch',
462            'plot'                : True,
463            'cast'                : 'Lino Ventura\n\
464Françoise Fabian\n\
465Charles Gérard\n\
466André Falcon',
467            'country'             : 'Frankreich/Italien',
468            'genre'               : 'Drama',
469            'classification'      : 'ab 12',
470            'studio'              : 'Black Hill Pictures',
471            'o_site'              : False,
472            'site'                : 'http://www.video.de/videofilm/ein-glueckliches-jahr-dvd/85546.html',
473            'trailer'             : False,
474            'year'                : 1973,
475            'notes'               : 'Sprachen:\n\
476Deutsch DD 2.0, Französisch DD 2.0\n\
477\n\
478Tonformat:\n\
479Dolby Digital 2.0\n\
480\n\
481Bildformat:\n\
4821:1,33/4:3',
483            'runtime'             : 110,
484            'image'               : True,
485            'rating'              : False,
486            'cameraman'           : 'Jean Collomb',
487            'screenplay'          : 'Claude Lelouch'
488        },
489        'V_arahan-vanilla-dvd/90405.html' : {
490            'title'               : 'Arahan',
491            'o_title'             : 'Arahan jangpung dae jakjeon',
492            'director'            : 'Ryoo Seung-wan',
493            'plot'                : True,
494            'cast'                : 'Ryu Seung-beom' + _(' as ') + 'Sang-hwan\n\
495Yoon So-yi' + _(' as ') + 'Wi-jin\n\
496Ahn Sung-kee' + _(' as ') + 'Ja-woon\n\
497Jung Doo-hong' + _(' as ') + 'Heuk-woon\n\
498Yun Ju-sang' + _(' as ') + 'Mu-woon',
499            'country'             : 'Südkorea',
500            'genre'               : 'Action/ Komödie',
501            'classification'      : 'ab 16',
502            'studio'              : 'Splendid Film',
503            'o_site'              : False,
504            'site'                : 'http://www.video.de/videofilm/arahan-vanilla-dvd/90405.html',
505            'trailer'             : False,
506            'year'                : 2004,
507            'notes'               : 'Sprachen:\n\
508Deutsch DD 5.1\n\
509\n\
510Tonformat:\n\
511Dolby Digital 5.1\n\
512\n\
513Bildformat:\n\
5141:1,78/16:9',
515            'runtime'             : 108,
516            'image'               : True,
517            'rating'              : False,
518            'cameraman'           : 'Lee Jun-gyu',
519            'screenplay'          : 'Ryoo Seung-wan'
520        }
521    }
522