﻿#betaseries.com 
#Subtitles service allowed by www.betaseries.com 
import re, urllib, urllib2, sys, hashlib
import cStringIO, StringIO
from os import path
from zipfile import ZipFile
from urlparse import urlunparse



OS_API = 'http://plexapp.api.opensubtitles.org/xml-rpc'
OS_LANGUAGE_CODES = 'http://www.opensubtitles.org/addons/export_languages.php'
OS_PLEX_USERAGENT = 'plexapp.com v9.0'
subtitleExt       = ['utf','utf8','utf-8','sub','srt','smi','rt','ssa','aqt','jss','ass','idx']

BS_PROTOCOL_HTTP = "https" 
BS_URL = 'api.betaseries.com'
BS_SITE = '%s://%s' % (BS_PROTOCOL_HTTP, BS_URL)
BS_API_VERSION = "2.2"
BS_API_KEY  = '5eedfb7e79df'
BS_SHOW_URL = '%s/shows/search' % (BS_SITE)
BS_EPISODE_URL = '%s/episodes/search' % (BS_SITE)
BS_SUBTITLES_URL = '%s/subtitles/episode' % (BS_SITE)
BS_MEMBERS_AUTH = "/members/auth.json"
BS_EPISODE_LIST = "/episodes/list"
BS_EPISODE_DOWNLOADED = "/episodes/downloaded"
BS_EPISODE_WATCHED = "/episodes/watched"
BS_MEMBERS_INFO = "/members/infos"
BS_SHOW_FOLLOW = "/shows/show"

HEADERS = {"User-agent": OS_PLEX_USERAGENT , "Accept": "application/json", "X-BetaSeries-Key": BS_API_KEY, "X-BetaSeries-Version": BS_API_VERSION}
AUTH_HEADERS = [("User-agent", OS_PLEX_USERAGENT) , ("X-BetaSeries-Version", BS_API_VERSION)]

#Class to identify different search mode
class OS_Search_Methode:
    Hash, IMDB, Name = range(0, 3)

# Function taken from TheTVDB metadata agent
#TODO: Perhaps it is possible to use the function directly with the @expose decorator.

def GetResultFromBS(url):

    #Log("Retrieving URL: " + url)

    try:
      result = JSON.ObjectFromURL(url, headers=HEADERS, timeout=60, sleep=0.5)
    except:
      Log("Error (%s) retrieving url: %s" % result, url)
      return None
    
    return result

def Start():
  HTTP.CacheTime = CACHE_1HOUR * 4
  HTTP.Headers['User-Agent'] = OS_PLEX_USERAGENT


@expose
def opensubtitlesProxy():
  proxy = XMLRPC.Proxy(OS_API)
  #username = Prefs["username"]
  #password = Prefs["password"]
  #if username == None or password == None:
  #  username = ''
  #  password = ''
  username = ''
  password = ''
  try:
    proxyResponse = proxy.LogIn(username, password, 'en', OS_PLEX_USERAGENT)
    if proxyResponse['status'] != "200 OK":
      Log('Error return by XMLRPC proxy: %s' % proxyResponse['status'])
      token = False
    else:
      token = proxyResponse['token']
  except Exception, e:
    Log('Unexpected error with OpenSubtitles.org XMLRPC API : %s' % str(e))
    token = False

 
  return (proxy, token)

def getLanguageOfPrimaryAgent(guid):
  #extract from the guid the language used by the primary_agent
  primaryAgentLanguage = None
  m = re.match(r'.*\?lang=(?P<primaryAgentLanguage>\w+)$', guid)
  if m != None:
    if m.group('primaryAgentLanguage') != False:
      primaryAgentLanguage = m.group('primaryAgentLanguage')
  return primaryAgentLanguage

def getIdfromTheTVDB(guid):
  #Extract from guid the primary data agent and the id of the show
  showTheTVDBId = False 
  m = re.match(r"(?P<primary_agent>.+):\/\/(?P<showID>\d+)", guid)
  if m != None:
    #Log("primary_agent: %s | showID: %s | seasonID: %s | episodeID: %s" % (m.group('primary_agent'), m.group('showID'), m.group('seasonID'), m.group('episodeID')))
    #If primary agent is TheTVDB, extract the tvshow and episode IMDN ids
    if m.group('primary_agent') ==  "com.plexapp.agents.thetvdb":
      #Fetch the TVDB show ID
      showTheTVDBId = m.group('showID')
                
  return  showTheTVDBId

def getShowIDFromBD(Title, TheTVDBShowId):
  showID = False
  try:
    url = "%s?title=%s" % (BS_SHOW_URL, urllib.quote_plus(Title)) 
    jsonShowBS = GetResultFromBS(url)
  except Exception, err:
    Log("Error searching show ID for %s on BetaSeries" % url)
    Log("Error : %s, line nr : %s", str(err), sys.exc_traceback.tb_lineno)
  try: 
    for shows in jsonShowBS.get("shows"):
       #Log("Show : %s" % shows)
       showID = shows.get("thetvdb_id")
       if int(showID) == int(TheTVDBShowId):
         return shows.get("id")
  except Exception, err:
    Log("Error get node shows: %s, line nr : %s", str(err), sys.exc_traceback.tb_lineno)
    return None
  return None

def getEpisodeIDFromBD(showID, seasonNr, episodeNr):
  episodeID = False
  seasonNr = "00" + seasonNr
  episodeNr = "00" + episodeNr
  numberEpisode = "s" + seasonNr[-2:] + "e" + episodeNr[-2:]
  try:
    url = "%s?show_id=%s&number=%s" % (BS_EPISODE_URL, showID, numberEpisode)
    jsonEpisodeBS = GetResultFromBS(url)
  except Exception, err:
    Log("Error searching episode ID for %s on BetaSeries" % showID)
    Log("Error : %s, line nr : %s", str(err), sys.exc_traceback.tb_lineno)
  try: 
    #Log("episode : %s" % jsonEpisodeBS)
    episode = jsonEpisodeBS.get("episode")
    episodeID = episode.get("id", [])
    Log("====> episode ID : %s" % episodeID)
    return episodeID
  except Exception, err:
    Log("Error get node episode: %s, line nr : %s", str(err), sys.exc_traceback.tb_lineno)
    return None
  return None

def downloadSubTitlesFromBS(showID, episodeID, language, mediaTitle, season, episode, part, token):
 
  try:
    url = "%s?id=%s" % (BS_SUBTITLES_URL, episodeID)
    jsonShowBS = GetResultFromBS(url)
  except Exception, err:
    Log("Error searching subtitles show for %s on BetaSeries" % url)
    Log("Error : %s, line nr : %s", str(err), sys.exc_traceback.tb_lineno)

  try: 
    for subtitles in jsonShowBS.get("subtitles", []):
      if subtitles.get("language") == "VF":
          episodes = subtitles.get("episode", [])
          Log("episode ID  found: %s" % subtitles["id"])
          subtitlesURL = subtitles["url"]
          Log("subtitles URL : %s" % subtitlesURL)
          (req, data) = getURLData(subtitlesURL)
          #Log("REturned headers : %s" % req.info())
          m = re.findall("(?<=filename=\").*?(?=\")", req.headers['Content-Disposition'])
          #Log("m : %s" % m[0])
          name = re.sub('\\\\(.)', '\1', m[0])
          ext = path.splitext(name)[1][1:].strip() 
          if ext.upper()=="ZIP":
            subtitle = cStringIO.StringIO(data)
            subtitleFileName = ZipFile(subtitle, 'r').namelist()[0]
            #Log("subtitleFileName in the zip : %s" % subtitleFileName)
            ext = path.splitext(subtitleFileName)[1][1:].strip() 
            srt = ZipFile(subtitle, 'r').open(subtitleFileName)
            Log("subtitle filename : %s" % subtitleFileName)
            part.subtitles['fr'][subtitlesURL]= Proxy.Media(srt.read(), ext)
            ZipFile(subtitle, 'r').close()
          elif ext not in subtitleExt:
             Log("Extension : %s not allowed", ext)
          else:
            Log("subtitle filename : %s" % m[0])
            part.subtitles['fr'][subtitlesURL]= Proxy.Media(data, ext)
            
          
  except Exception, err:
    Log("Error getting node subtitles: %s, line nr : %s", str(err), sys.exc_traceback.tb_lineno)
    return None

def getWatchedStatus(serverIP, PlexID):
  url = "http://%s:32400/library/metadata/%s?checkFiles=1" % (serverIP, PlexID)
  #Log("URL : %s" % url)
  (req, data) = getURLData(url)
  m = re.findall("(.viewCount=\"\d\")", data)
  return m

def getURLData(url):
    #Convert the returns of queries in commonly exploitable data.
    
    req = urllib2.urlopen(url)
    data = req.read()
    return (req, data)

def auth_getURL(method, params=None):
    scheme = BS_PROTOCOL_HTTP
    netloc = BS_URL
    # methode de l'api avec le format de retour souhaitÃ© (xml|json)
    format = "json"
    #path = '%s.%s' % (method, format)
    path = '%s' % method
    # insertion de la clÃ© api
    param_key = "key=%s" % BS_API_KEY
    # construction des paramÃ¨tres de l'url
    query = '%s&%s' % (param_key, params)
    # retourne l'ensemble de l'url
    return urlunparse((scheme, netloc, path,None, query, None))

def auth_getSource(url, data=None):
    opener = urllib2.build_opener()
    # ajout de l'user-agent
    opener.addheaders = AUTH_HEADERS
    Log("URL : %s" % url)
    #Log("HEADER : %s" % opener.getheaders)
    if data:
      source = opener.open(url, data)
    else:
      source = opener.open(url)
    # retourne le contenu
    return source.read()


def auth_getData(url, data=None):
    if data:
      source = auth_getSource(url, data)
    else:
      source = auth_getSource(url)
    #json_data = json.loads(source)
    json_data = JSON.ObjectFromString(source)
    # retourne le contenu sous forme de dictionnaire Python
    return json_data
    #return source

def members_auth():
    username = Prefs["username"]
    password = Prefs["password"]
    if username == None or password == None:
        return None
    else:
        # hash MD5 du mot de passe
        hash_pass = hashlib.md5(password).hexdigest()
        params = urllib.urlencode({'login': username, 'password': hash_pass})
        url = auth_getURL(BS_MEMBERS_AUTH, params)
        return auth_getData(url)

class BetaSeriesAgentTV(Agent.TV_Shows):
  name = 'BetaSeries.com'
  languages = [Locale.Language.NoLanguage]
  primary_provider = False
  contributes_to = ['com.plexapp.agents.thetvdb']

  def search(self, results, media, lang):
    results.Append(MetadataSearchResult(
      id    = 'null',
      score = 100
    ))

  def update(self, metadata, media, lang):
    BSqueue = []
    serverIP = Network.Address      
    BSCreateSerie = Prefs["createserie"]
    (proxy, token) = opensubtitlesProxy()
    auth_token = members_auth()
    if auth_token != None:
      Log("Token : %s" % auth_token['root']['member']['token'])
    else:
      Log("No authentication on BetaSeries, only downloading subtitles")

    if token == False:
      Log('Subtitles search stop due to a problem with the OpenSubtitles API')
    else:
      TheTVDBShowId = getIdfromTheTVDB(media.guid)
      for season in media.seasons:
        # just like in the Local Media Agent, if we have a date-based season skip for now.
        if int(season) < 1900:
          for episode in media.seasons[season].episodes:
            for i in media.seasons[season].episodes[episode].items:
              primaryAgentLanguage = getLanguageOfPrimaryAgent(media.seasons[season].episodes[episode].guid)
              for part in i.parts:
                # Remove all previous subs (taken from sender1 fork)
                try:
                  for l in part.subtitles:
                    part.subtitles[l].validate_keys([])
                except: None

                # go fetch subtitle
                for language in ['fr']:
                  showID = getShowIDFromBD(media.title, TheTVDBShowId)
                  if showID != None:
                    # Check if Show is followed on BetaSeries and if not, check if it has to be marked as followed in de preferences
                    if BSCreateSerie == True and auth_token != None:
                       params = urllib.urlencode({'token': auth_token['root']['member']['token'], 'v': BS_API_VERSION})
                       url = auth_getURL(BS_MEMBERS_INFO, params)
                       BSInfo = auth_getData(url)
                       notFound = True
                       members = BSInfo.get("member")
                       for shows in members.get("shows",[]):
                          if showID == shows.get("id", []):
                             notFound = False
                             break
                       if notFound == True:
                          Log("Mark this show (%s) as followed on BetaSeries.com" % showID)
                          params = urllib.urlencode({'token': auth_token['root']['member']['token']})
                          data = urllib.urlencode({'id': showID})
                          url = auth_getURL(BS_SHOW_FOLLOW, params)
                          showFollowed = auth_getData(url, data)
                                  
                    episodeID = getEpisodeIDFromBD(showID, season, episode)
                    if episodeID != None:
                       downloadSubTitlesFromBS(showID, episodeID, language, media.title, season, episode, part, token)
                       PlexID = media.seasons[season].episodes[episode].id
                       isWatched = getWatchedStatus(serverIP, PlexID)
                       Log("Episode to mark as watched on BetaSeries")
                       if isWatched and auth_token != None:
                         params = urllib.urlencode({'token': auth_token['root']['member']['token'], 'showID': showID})
                         url = auth_getURL(BS_EPISODE_LIST, params)
                         BSlist = auth_getData(url)
                         #Log("***** BS LIST : %s" % BSlist)
                         
                         #TODO : 
                         # - check if correct ShowID
                         
                         # Mark as watched and downloaded
                         for shows in BSlist.get("shows"):
                           for unseens in shows.get("unseen"):  
                             #Log("Unseen ID : %s, episode ID : %s" % (unseens['id'], episodeID))
                             if unseens['id'] == episodeID and unseens['user']['downloaded'] == False:
                               Log("Mark this episode (%s) as downloaded on BetaSeries.com" % unseens['id'])
                               params = urllib.urlencode({'token': auth_token['root']['member']['token']})
                               data = urllib.urlencode({'id': episodeID})
                               url = auth_getURL(BS_EPISODE_DOWNLOADED, params)
                               episodeDownloaded = auth_getData(url, data)
                               #Log("Return downloaded : %s" % episodeDownloaded)
                             if unseens['id'] == episodeID and unseens['user']['seen'] == False:
                               Log("Mark this episode (%s) as watched on BetaSeries.com" % unseens['id'])
                               params = urllib.urlencode({'token': auth_token['root']['member']['token']})
                               data = urllib.urlencode({'id': episodeID, 'bulk':'false'})
                               url = auth_getURL(BS_EPISODE_WATCHED, params)
                               episodeWatched = auth_getData(url, data)
                               #Log("Return downloaded : %s" % episodeWatched)
                    else:
                      Log("No episode ID find with TheTVDB ID! Stopping downloading of subtitles for this episode")
                  else:
                    Log("No show ID find with TheTVDB ID! Stopping downloading of subtitles for this episode")

    