kodi-czsk/plugin.video.sosac.ph

View on GitHub
resources/lib/sosac.py

Summary

Maintainability
F
4 days
Test Coverage
# -*- coding: UTF-8 -*-
# /*
# *      Copyright (C) 2015 Libor Zoubek + jondas
# *
# *
# *  This Program is free software; you can redistribute it and/or modify
# *  it under the terms of the GNU General Public License as published by
# *  the Free Software Foundation; either version 2, or (at your option)
# *  any later version.
# *
# *  This Program is distributed in the hope that it will be useful,
# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# *  GNU General Public License for more details.
# *
# *  You should have received a copy of the GNU General Public License
# *  along with this program; see the file COPYING.  If not, write to
# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
# *  http://www.gnu.org/copyleft/gpl.html
# *
# */

import urllib
import urllib2
import cookielib
import hashlib
import sys
import json
import datetime
import re

import util
from provider import ContentProvider, cached, ResolveException

sys.setrecursionlimit(10000)

MOVIES_BASE_URL = "http://movies.prehraj.me"
TV_SHOW_FLAG = "#tvshow#"
ISO_639_1_CZECH = "cs"
ALPHA_SORT = '1'
YEAR_SORT = '2'

# JSONs
URL = "http://tv.sosac.to"
J_MOVIES_A_TO_Z_TYPE = "/vystupy5981/souboryaz.json"
J_MOVIES_GENRE = "/vystupy5981/souboryzanry.json"
J_MOVIES_MOST_POPULAR = "/vystupy5981/moviesmostpopular.json"
J_MOVIES_RECENTLY_ADDED = "/vystupy5981/moviesrecentlyadded.json"
# hack missing json with a-z series
J_TV_SHOWS_A_TO_Z_TYPE = "/vystupy5981/tvpismenaaz/"
J_TV_SHOWS = "/vystupy5981/tvpismena/"
J_SERIES = "/vystupy5981/serialy/"
J_TV_SHOWS_MOST_POPULAR = "/vystupy5981/tvshowsmostpopular.json"
J_TV_SHOWS_RECENTLY_ADDED = "/vystupy5981/tvshowsrecentlyadded.json"
J_SEARCH = "/jsonsearchapi.php?q="
STREAMUJ_URL = "http://www.streamuj.tv/video/"
IMAGE_URL = "http://movies.sosac.tv/images/"
IMAGE_MOVIE = IMAGE_URL + "75x109/movie-"
IMAGE_SERIES = IMAGE_URL + "558x313/serial-"
IMAGE_EPISODE = URL
IMAGE_DUBBING = "http://movies.sosac.tv/web-icons/sounds/"

FILTER_URL_PARAM = "?filter"
DUBBING_URL_PARAM = "?dub="
DUBBING_REGEX = r"\?dub=(\w\w)"
DUBBING_SETTINGS = [
    ["cs", "CZECH"],
    ["en", "ENGLISH"],
    ["sk", "SLOVAK"],
    ["cn", "CHINES"],
    ["de", "GERMAN"],
    ["el", "GREEK"],
    ["es", "SPANISH"],
    ["fi", "FINNISH"],
    ["fr", "FRENCH"],
    ["hr", "CROATIAN"],
    ["id", "INDONESIAN"],
    ["it", "ITALIAN"],
    ["ja", "JAPANES"],
    ["ko", "KOREAN"],
    ["nl", "DUTCH"],
    ["no", "NORWEGIAN"],
    ["pl", "POLISH"],
    ["pt", "PORTUGUESE"],
    ["ru", "RUSSIAN"],
    ["tr", "TURKISH"],
    ["vi", "VIETNAMESE"]
]

LIBRARY_MENU_ITEM_ADD = "[B][COLOR red]Add to library[/COLOR][/B]"
LIBRARY_MENU_ITEM_ADD_ALL = "[B][COLOR red]Add all to library[/COLOR][/B]"
LIBRARY_MENU_ITEM_REMOVE = "[B][COLOR red]Remove from subscription[/COLOR][/B]"
LIBRARY_MENU_ITEM_REMOVE_ALL = "[B][COLOR red]Remove all subscriptions[/COLOR][/B]"
LIBRARY_TYPE_VIDEO = "video"
LIBRARY_TYPE_TVSHOW = "tvshow"
LIBRARY_TYPE_ALL_VIDEOS = "all-videos"
LIBRARY_TYPE_RECENT_VIDEOS = "recent-videos"
LIBRARY_TYPE_ALL_SHOWS = "all-shows"
LIBRARY_ACTION_ADD = "add-to-library"
LIBRARY_ACTION_ADD_ALL = "add-all-to-library"
LIBRARY_ACTION_REMOVE_ALL = "remove-all-from-library"
LIBRARY_ACTION_REMOVE_SUBSCRIPTION = "remove-subscription"
LIBRARY_FLAG_IS_PRESENT = "[B][COLOR yellow]*[/COLOR][/B] "

RATING = 'r'
LANG = 'd'
QUALITY = 'q'
IMDB = 'm'
CSFD = 'c'
DESCRIPTION = 'p'
GENRE = 'g'

RATING_STEP = 2


class SosacContentProvider(ContentProvider):
    ISO_639_1_CZECH = None
    par = None

    def __init__(self, username=None, password=None, filter=None, reverse_eps=False,
                 force_czech=False, order_recently_by=0):
        ContentProvider.__init__(self, name='sosac.ph', base_url=MOVIES_BASE_URL, username=username,
                                 password=password, filter=filter)
        opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookielib.LWPCookieJar()))
        urllib2.install_opener(opener)
        self.reverse_eps = reverse_eps
        self.force_czech = force_czech
        self.streamujtv_user = None
        self.streamujtv_pass = None
        self.streamujtv_location = None
        self.order_recently_by = order_recently_by

    def on_init(self):
        kodilang = self.lang or 'cs'
        if kodilang == ISO_639_1_CZECH or kodilang == 'sk' or self.force_czech:
            self.ISO_639_1_CZECH = ISO_639_1_CZECH
        else:
            self.ISO_639_1_CZECH = 'en'

    def capabilities(self):
        return ['resolve', 'categories', 'search']

    def categories(self):
        result = []
        item = self.dir_item(title="Movies", url=URL + J_MOVIES_A_TO_Z_TYPE)
        item['menu'] = {
            LIBRARY_MENU_ITEM_ADD_ALL: {
                'action': LIBRARY_ACTION_ADD_ALL,
                'type': LIBRARY_TYPE_ALL_VIDEOS
            }
        }
        result.append(item)

        item = self.dir_item(title="TV Shows", url=URL +
                             J_TV_SHOWS_A_TO_Z_TYPE)
        item['menu'] = {
            LIBRARY_MENU_ITEM_ADD_ALL: {
                'action': LIBRARY_ACTION_ADD_ALL,
                'type': LIBRARY_TYPE_ALL_SHOWS
            }
        }
        result.append(item)

        item = self.dir_item(title="Movies - by Genres", url=URL +
                             J_MOVIES_GENRE)
        result.append(item)

        item = self.dir_item(title="Movies - Most popular", url=URL +
                             J_MOVIES_MOST_POPULAR)
        result.append(item)

        item = self.dir_item(title="TV Shows - Most popular", url=URL +
                             J_TV_SHOWS_MOST_POPULAR)
        result.append(item)

        item = self.item_with_last_mod("Movies - Recently added", URL + J_MOVIES_RECENTLY_ADDED)
        item['menu'] = {
            LIBRARY_MENU_ITEM_ADD_ALL: {
                'action': LIBRARY_ACTION_ADD_ALL,
                'type': LIBRARY_TYPE_RECENT_VIDEOS
            }
        }
        result.append(item)

        for item in result:
            if 'menu' not in item:
                item['menu'] = {}
            item['menu'][LIBRARY_MENU_ITEM_REMOVE_ALL] = {
                'action': LIBRARY_ACTION_REMOVE_ALL
            }

        item = self.item_with_last_mod("TV Shows - Recently added", URL + J_TV_SHOWS_RECENTLY_ADDED)
        result.append(item)

        return result

    def search(self, keyword):
        if len(keyword) < 3 or len(keyword) > 100:
            return [self.dir_item(title="Search query must be between 3 and 100 characters long!",
                                  url="fail")]
        return self.list_videos(URL + J_SEARCH + urllib.quote_plus(keyword))

    def a_to_z(self, url):
        result = []
        for letter in ['0-9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                       'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']:
            item = self.dir_item(title=letter.upper())
            item['url'] = URL + url + letter + ".json"
            result.append(item)
        return result

    @staticmethod
    def particular_letter(url):
        return "a-z/" in url

    def has_tv_show_flag(self, url):
        return TV_SHOW_FLAG in url

    def list(self, url, filter=None):
        util.info("Examining url " + url)

        list_result = None
        if FILTER_URL_PARAM in url:
            list_result = self.list_movies_by_dubbing(url)
        elif not filter and DUBBING_URL_PARAM in url:
            list_result = self.list_dubbing(url)
        elif J_MOVIES_A_TO_Z_TYPE in url or J_MOVIES_GENRE in url:
            list_result = self.load_json_list(url)
        elif J_SERIES in url:
            list_result = self.list_episodes(url)
        elif J_TV_SHOWS in url or J_TV_SHOWS_MOST_POPULAR in url:
            list_result = self.list_series_letter(url)
        elif J_TV_SHOWS_RECENTLY_ADDED in url:
            list_result = self.list_recentlyadded_episodes(url)
        elif J_TV_SHOWS_A_TO_Z_TYPE in url:
            list_result = self.a_to_z(J_TV_SHOWS)
        else:
            order_by = None
            if J_MOVIES_RECENTLY_ADDED in url:
                order_by = self.order_recently_by
            list_result = self.list_videos(url, filter, order_by)
        return list_result

    def list_dubbing(self, url):
        p = re.compile(DUBBING_REGEX)
        m = p.search(url)
        dub = m.group(1)
        url_without_dub = url[:m.start()]

        filter = self.has_video_dub(dub)
        return self.list(url_without_dub, filter)

    def load_json_list(self, url):
        result = []
        data = util.request(url)
        json_list = json.loads(data)
        for key, value in json_list.iteritems():
            item = self.dir_item(title=key.title())
            item['url'] = value
            result.append(item)

        return sorted(result, key=lambda i: i['title'])

    def list_videos(self, url, filter=None, order_by=0, library=False):
        result = []
        data = util.request(url)
        json_video_array = json.loads(data)
        for video in json_video_array:
            if not filter or filter(video):
                item = self.video_item()
                item['title'] = self.get_video_name(video)
                item['img'] = IMAGE_MOVIE + video['i']
                item['url'] = video['l'] if video['l'] and video['l'] is not None else ""
                item['year'] = int(video['y'])
                if RATING in video:
                    item['rating'] = video[RATING] * RATING_STEP
                if LANG in video:
                    item['lang'] = video[LANG]
                if QUALITY in video and video[QUALITY] is not None:
                    item['quality'] = video[QUALITY]
                if GENRE in video:
                    item['plot'] = ' '.join(video[GENRE])
                item['menu'] = {
                    LIBRARY_MENU_ITEM_ADD: {
                        'url': item['url'],
                        'type': LIBRARY_TYPE_VIDEO,
                        'action': LIBRARY_ACTION_ADD,
                        'name': self.get_library_video_name(video)
                    }
                }
                if CSFD in video and video[CSFD] is not None:
                    item['menu'][LIBRARY_MENU_ITEM_ADD]['csfd'] = video[CSFD]
                if IMDB in video and video[IMDB] is not None:
                    item['menu'][LIBRARY_MENU_ITEM_ADD]['imdb'] = video[IMDB]
                result.append(item)
        util.debug("ORDER BY" + str(order_by))
        if order_by == ALPHA_SORT:
            result = sorted(result, key=lambda i: i['title'])
        elif order_by == YEAR_SORT:
            result = sorted(result, key=lambda i: i['year'], reverse=True)
        if not filter and not library:
            result.insert(0, self.dir_item(title="Filter", url=url + FILTER_URL_PARAM))
        return result

    def list_series_letter(self, url, load_subs=True):
        result = []
        data = util.request(url)
        json_list = json.loads(data)
        subs = self.get_subscriptions() if load_subs else {}
        for serial in json_list:
            item = self.dir_item()
            item['title'] = self.get_localized_name(serial['n'])
            item['img'] = IMAGE_SERIES + serial['i']
            item['url'] = serial['l']
            item['year'] = int(serial['y'])
            if DESCRIPTION in serial:
                item['plot'] = serial[DESCRIPTION].encode('utf-8')
            if RATING in serial:
                item['rating'] = serial[RATING] * RATING_STEP
            if item['url'] in subs:
                item['title'] = LIBRARY_FLAG_IS_PRESENT + item['title']
                item['menu'] = {
                    LIBRARY_MENU_ITEM_REMOVE: {
                        'url': item['url'],
                        'action': LIBRARY_ACTION_REMOVE_SUBSCRIPTION,
                        'name': self.get_library_video_name(serial)
                    }
                }
            else:
                item['menu'] = {
                    LIBRARY_MENU_ITEM_ADD: {
                        'url': item['url'],
                        'type': LIBRARY_TYPE_TVSHOW,
                        'action': LIBRARY_ACTION_ADD,
                        'name': self.get_library_video_name(serial)
                    }
                }
                if CSFD in serial and serial[CSFD] is not None:
                    item['menu'][LIBRARY_MENU_ITEM_ADD]['csfd'] = serial[CSFD]
                if IMDB in serial and serial[IMDB] is not None:
                    item['menu'][LIBRARY_MENU_ITEM_ADD]['imdb'] = serial[IMDB]
            result.append(item)
        return result

    def list_movies_by_dubbing(self, url):
        url_without_filter = url[:-len(FILTER_URL_PARAM)]

        result = []
        for i in DUBBING_SETTINGS:
            item = self.dir_item(title=i[1], url=url_without_filter + DUBBING_URL_PARAM + i[0])
            item['img'] = IMAGE_DUBBING + i[0] + ".png"
            result.append(item)
        return result

    def list_episodes(self, url):
        result = []
        data = util.request(url)
        json_series = json.loads(data)
        for series in json_series:
            for series_key, episode in series.iteritems():
                for episode_key, video in episode.iteritems():
                    item = self.video_item()
                    item['title'] = (series_key.zfill(2) + "x" + episode_key.zfill(2) +
                                     " - " + video['n'])
                    item['season'] = int(series_key)
                    item['episode'] = int(episode_key)
                    if video['i'] is not None:
                        item['img'] = IMAGE_EPISODE + video['i']
                    item['url'] = video['l'] if video['l'] else ""
                    result.append(item)
        if not self.reverse_eps:
            result.reverse()
        return result

    def list_recentlyadded_episodes(self, url):
        result = []
        data = util.request(url)
        json_series = json.loads(data)
        for episode in json_series:
            item = self.video_item()
            item['title'] = self.get_episode_recently_name(episode)
            item['season'] = int(episode['s'])
            item['episode'] = int(episode['e'])
            item['img'] = IMAGE_EPISODE + episode['i']
            item['url'] = episode['l']
            result.append(item)
        return result

    def library_list_all_videos(self, filter=None):
        letters = self.load_json_list(URL + J_MOVIES_A_TO_Z_TYPE)
        total = len(letters)

        step = int(100 / total)
        for idx, letter in enumerate(letters):
            for video in self.list_videos(letter['url'], filter=filter, library=True):
                yield video
            yield {'progress': step * (idx + 1)}

    def library_list_recent_videos(self, filter=None):
        videos = self.list_videos(URL + J_MOVIES_RECENTLY_ADDED, filter, library=True)
        total = len(videos)

        step = int(100 / total)
        for idx, video in enumerate(videos):
            yield video
            yield {'progress': step * (idx + 1)}

    def library_list_all_tvshows(self):
        letters = self.a_to_z(J_TV_SHOWS)
        total = len(letters)

        step = int(100 / total)
        for idx, letter in enumerate(letters):
            for video in self.list_series_letter(letter['url'], False):
                yield video
            yield {'progress': step * (idx + 1)}

    def get_video_name(self, video):
        name = self.get_localized_name(video['n'])
        year = (" (" + video['y'] + ")") if video['y'] else " "
        quality = (" - " + video[QUALITY].upper()) if video[QUALITY] else ""
        return name + year + quality

    def get_library_video_name(self, video):
        name = self.get_localized_name(video['n'])
        year = (" (" + video['y'] + ")") if video['y'] else " "
        return (name + year).encode('utf-8')

    def get_episode_recently_name(self, episode):
        serial = self.get_localized_name(episode['t']) + ': '
        series = episode['s'].zfill(2) + "x"
        number = episode['e'].zfill(2) + " - "
        name = self.get_localized_name(episode['n'])
        return serial + series + number + name

    def get_localized_name(self, names):
        if self.ISO_639_1_CZECH in names:
            return names[self.ISO_639_1_CZECH]
        return names[ISO_639_1_CZECH]

    def resolve(self, item, captcha_cb=None, select_cb=None):
        data = item['url']
        if not data:
            raise ResolveException('Video is not available.')
        result = self.findstreams([STREAMUJ_URL + data])
        stream = None
        if len(result) == 1:
            stream = result[0]
        elif len(result) > 1 and select_cb:
            stream = select_cb(result)
            if not stream:
                return None
        return self.probe_html5(self.set_streamujtv_info(stream))

    def probe_html5(self, result):

        class NoRedirectHandler(urllib2.HTTPRedirectHandler):

            def http_error_302(self, req, fp, code, msg, headers):
                infourl = urllib.addinfourl(fp, headers, req.get_full_url())
                infourl.status = code
                infourl.code = code
                return infourl
            http_error_300 = http_error_302
            http_error_301 = http_error_302
            http_error_303 = http_error_302
            http_error_307 = http_error_302

        opener = urllib2.build_opener(NoRedirectHandler())
        urllib2.install_opener(opener)

        r = urllib2.urlopen(urllib2.Request(result['url'], headers=result['headers']))
        if r.code == 200:
            result['url'] = r.read()
        return result

    def set_streamujtv_info(self, stream):
        if stream:
            if len(self.streamujtv_user) > 0 and len(self.streamujtv_pass) > 0:
                # set streamujtv credentials
                m = hashlib.md5()
                m.update(self.streamujtv_pass)
                h = m.hexdigest()
                m = hashlib.md5()
                m.update(h)
                stream['url'] = stream['url'] + \
                    "&pass=%s:::%s" % (self.streamujtv_user, m.hexdigest())
            if self.streamujtv_location in ['1', '2']:
                stream['url'] = stream['url'] + "&location=%s" % self.streamujtv_location
        return stream

    def get_subscriptions(self):
        return self.parent.get_subs()

    def request_last_update(self, url):
        util.debug('request: %s' % url)
        lastmod = None
        req = urllib2.Request(url)
        req.add_header('User-Agent', util.UA)
        try:
            response = urllib2.urlopen(req)
            lastmod = datetime.datetime(*response.info().getdate('Last-Modified')[:6]).strftime(
                '%d.%m.%Y')
            response.close()
        except urllib2.HTTPError, error:
            util.debug(error.read())
            error.close()
        return lastmod

    def item_with_last_mod(self, title, url):
        lastmod = self.request_last_update(url)
        if lastmod:
            title += " (" + lastmod + ")"
        item = self.dir_item(title=title, url=url)
        return item

    def has_video_dub(self, dubbing):
        return lambda v: LANG in v and dubbing in v[LANG]