# -*- coding: utf-8 -*-
#
#  Copyright 2022 Machine Learning and Language Processing (MLLP) research group
#                 Universitat Politècnica de València (UPV)
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

"""

## The *transLectures-UPV Platform* (TLP) Python3 API client module

**tlp-client** module mainly implements the `TLPSpeechClient`,
`TLPTextClient` and `TLPWebClient` classes that provides
several methods to interact with the `/speech`, `/text` and `/web` APIs of the
*transLectures-UPV Platform*.

Below are shown some examples on interacting with the `/speech`
API. Using this library to query `/text` and `/web` API interfaces is pretty similar.

First, we have to import the `tlp_client` library and create a
`TLPSpeechClient` class instance, using the appropriate argument values. E.g.:

    >>> import tlp_client
    >>> tlp = tlp_client.TLPSpeechClient("https://my-tlp-instance.com/api/v3", 
                                         "https://my-tlp-instance.com/player", 
                                         "my-api-user", 
                                         "my-api-key")

To **get the list of subtitle languages available** for a particular Media ID:

    >>> tlp.api_langs("media-id-1")
    >>> print(tlp.get_printable_response_data())

To **download a subtitles file**, for instance, English subtitles in SRT format:

    >>> tlp.api_get("media-id-1", "en", form="srt")
    >>> tlp.save_response_data("/path/to/my/media_subs.en.srt")

To **Generate and upload a Media Package File** to the TLP Server is also quite straightforward:
   
    >>> # Initialise Manifest, definining media ID:
    >>> tlp.manifest_init("media-id-2")
    >>> # Add some metadata (media language and title are mandatory)
    >>> tlp.manifest_set_metadata(language="en", title="Testing tlp_client")
    >>> # Add video/audio file (can be a public URL/URI, also)
    >>> tlp.manifest_set_main_media_file("/path/to/my/media_file.mp4")
    >>> # Enable Ingest Service's Test Mode, since we are doing a test:
    >>> tlp.manifest_set_options(test_mode=True)
    >>> # Generate Media Package File and upload it via the /ingest/new interface:
    >>> tlp.api_ingest_new()
    >>> # Why to print the response? I just want the upload ID!
    >>> upload_id = tlp.ret_data['id']

To **track the progress** of our first upload: 

    >>> tlp.api_status(upload_id)
    >>> print(tlp.get_printable_response_data())
    
You can build the most **complex Media Package** in the world, but only if you want!
   
    >>> tlp.manifest_init("media-id-3")
    >>> tlp.manifest_set_metadata(language="en", title="Really testing tlp_client")
    >>> tlp.manifest_set_main_media_file("/path/to/my/media_file.mp4")
    >>> # Add a related text file to improve transcription quality
    >>> tlp.manifest_add_attachment("/path/to/my/doc_file.pdf", 
                                     tlp_client.TLPClient.FTYPE_CODE_DOCUMENT)
    >>> # Request Spanish subtitles (English are generated by default):
    >>> tlp.manifest_add_subtitles_request("es")
    >>> # Request also a text-to-speech synthesized Spanish audiotrack:
    >>> tlp.manifest_add_audiotrack_request("es")
    >>> # ...
    >>> # AND FINALLY...
    >>> tlp.api_ingest_new()
    >>> # Mere curiosity... print me the generated Manifest JSON object!
    >>> print(tlp.get_printable_manifest())

Extended documentation of this module can be find either [here](https://ttp.mllp.upv.es/doc/clients/python3) or running `help(tlp_client)`.

For **further information** you might check the [full documentation of the transLectures-UPV Platform](https://ttp.mllp.upv.es/doc).
"""
 
import io
from datetime import datetime, timedelta
import hashlib
import json
import base64
import os.path
import tempfile
import zipfile
import shutil
import urllib.request, urllib.error, urllib.parse
import requests

class TLPClient():
    """
    TLPClient class.
    """

    TLP_VERSION = "3.X"
    API_SPEECH = "speech"
    API_TEXT = "text"
    API_WEB = "web"


    def __init__(self, api_base_url, editor_base_url, api_user, api_secret_key, api_type):
        """
        Creates a TLPClient class instance. Parameters:

        - **_api_base_url_**: Base URL to the transLectures-UPV Platform's API.
        - **_editor_base_url_**: Base URL to the transLectures-UPV Platform's Editor.
        - **_api_user_**: API username / TLP username.
        - **_api_secret_key_**: API Secret Key.
        - **_api_type_**: API type, must be `TLPClient.API_SPEECH`, `TLPClient.API_TEXT` or `TLPClient.API_WEB`.
        """
        self._last_api_call = None
        self._tmp_dir = None
        self._session = None
        self._response = None
        self._http_auth_enabled = False
        self._http_user = None
        self._http_pass = None
        self._api_base_url = api_base_url
        self._api_type = api_type
        self._editor_base_url = editor_base_url
        self._api_user = api_user
        self._api_secret_key = api_secret_key
        self.use_get_method = False
        """Use HTTP GET Method instead of POST."""
        self.use_data_parameter = True
        """Use a single "data" parameter on HTTP GET calls instead multiple GET parameters."""
        self.parameters = None
        """Dictionary with API query parameters."""
        self.ret_data = None
        """Data returned by the last performed API call. If the response was a JSON object, *ret_data* becomes a a parsed JSON object (dictionary or list, depending on the JSON)."""
        self.ret_content_type = None
        """Mime-type of the data returned by the last performed API call."""
        self.debug = False
        """Enable/Disable generation of debug information."""
        self.debug_info = None
        """Debug information of the last performed API call / Generation of Player URL."""

    def _reset_http_session(self):
        """Resets requests.Session class instance."""
        self._session = requests.Session()
        if self._http_auth_enabled:
            self._session.auth = (self._http_user, self._http_pass)

    def _reset(self):
        """Reset all class methods to perform a new API call."""
        self._reset_http_session()
        self._response = None
        self.parameters = {}
        self.parameters["client"] = "tlp_client-%s" % (self.TLP_VERSION)
        self.ret_data = None
        self.ret_content_type = None
        self.debug_info = None

    def _set_auth_parameters_secret_key(self):
        """Set API authentication parameters using the secret key."""
        self.parameters['user'] = self._api_user
        self.parameters['auth_token'] = self._api_secret_key

    def _set_auth_parameters_request_key_api(self, req_key_lifetime, oid, author=None, author_conf=None):
        """Set API authentication parameters using a request key for an API call."""
        self.parameters['user'] = self._api_user
        expire = self._gen_expire_parameter(req_key_lifetime)
        self.parameters['auth_token'] = self.gen_request_key(oid, expire, author, author_conf)
        self.parameters['expire'] = expire

    def _set_auth_parameters_request_key_editor(self, req_key_lifetime, oid, author=None, author_conf=None):
        """Set API authentication parameters using a request key for generating am Editor URL."""
        self.parameters['api_user'] = self._api_user
        expire = self._gen_expire_parameter(req_key_lifetime)
        self.parameters['request_key'] = self.gen_request_key(oid, expire, author, author_conf)
        self.parameters['expire'] = expire


    def _gen_expire_parameter(self, lifetime):
        """Returns a valid value of the "expire" API authentication parameter."""
        return int((datetime.utcnow() + timedelta(minutes=lifetime) - datetime(1970,1,1)).total_seconds())

    def _generate_debug_info_api(self, url):
        """ Generates debug information about the last API call."""
        o = io.StringIO()
        o.write("\n")
        o.write("- Input data (JSON-formatted):\n")
        o.write("%s\n" % json.dumps(self.parameters, indent=4))
        o.write("\n")
        if self.use_get_method:
          if self.use_data_parameter:
            o.write("- URL (using GET request with a single 'data' base64-encoded JSON parameter):\n")
            o.write("%s?data=%s\n" % (url, base64.b64encode(json.dumps(self.parameters))))
            o.write("\n")
          else:
            o.write("- URL (using GET request with multiple parameters):\n")
            params = "&".join([ "%s=%s"%(k, str(self.parameters[k])) for k in self.parameters ])
            o.write("%s?%s\n" % (url, params))
            o.write("\n")
        else:
          o.write("- URL + POST data:\n")
          o.write("%s\n" % url)
          o.write("%s\n" % json.dumps(self.parameters, indent=4))
        if self._last_api_call == "ingest":
          if self.manifest != None:
            o.write("\n- Manifest.JSON file:\n")
            o.write("%s\n" % self.get_printable_manifest())
            o.write("\n")
          if self._tmp_dir != None:
            o.write("- Temp dir: %s\n" % self._tmp_dir)
        o.write("\n- TLP Server response:\n")
        self.debug_info = o.getvalue()
 
    def _generate_debug_info_editor(self):
        """ Generates debug information about the last generated Player URL."""
        o = io.StringIO()
        o.write("\n")
        o.write("- Input data (JSON-formatted):\n")
        o.write("%s\n" % json.dumps(self.parameters, indent=4))
        o.write("\n")
        o.write("- Input data (Base 64):\n")
        o.write("%s\n" % base64.b64encode(json.dumps(self.parameters).encode('utf-8')).decode())
        o.write("\n")
        self.debug_info = o.getvalue()

    def _parse_response(self):
        if self._response.status_code == 200:
            self.ret_content_type = self._response.headers['content-type']
            if self.ret_content_type.find("application/json") >= 0:
                self.ret_data = json.loads(self._response.content)
            else:
                self.ret_data = self._response.content.decode('utf-8')
        elif self._response.status_code == 400:
            raise APIBadRequest(self._response.content)
        elif self._response.status_code == 401:
            raise APIUnauthorized(self._response.content)
        elif self._response.status_code == 419:
            raise APIAuthenticationTimeout(self._response.content)
        elif self._response.status_code == 500:
            raise APIInternalServerError(self._response.content)
        
   
    def _call(self, interface):
        """ Call to the specified interface with the parameters determined by the "parameters" class attribute."""
        url = "%s/%s/%s" % (self._api_base_url, self._api_type, interface)
        if self.use_get_method:
            if self.use_data_parameter:
                data = base64.b64encode(json.dumps(self.parameters))
                par = {'data':data}
            else:
                par = self.parameters
            self._response = self._session.get(url=url,params=par, verify=False)
        else:
            self._response = self._session.post(url=url,data=json.dumps(self.parameters), headers={'Content-Type':'application/json'}, verify=False)
        self._parse_response()
        self._last_api_call = interface
        if self.debug: self._generate_debug_info_api(url)

     
    def _call_ingest(self, interface, pkg=None):
        """ Call the specified interface using with the parameters determined by the "parameters" class attribute."""
        url = "%s/%s/%s" % (self._api_base_url, self._api_type, interface)
        self.parameters['manifest'] = self.manifest
        if pkg != None:
            files = {'json':('data.json', json.dumps(self.parameters), 'application/json')}
            files['pkg'] = ('mp.zip', open(pkg, 'rb'), 'application/zip')
            self._response = self._session.post(url=url,files=files, verify=False)
        else:
            self._response = self._session.post(url=url,data=json.dumps(self.parameters), headers={'Content-Type':'application/json'}, verify=False)
            
        self._parse_response()
        self._last_api_call = interface
        if self.debug: 
            self._generate_debug_info_api(url)
        else:
            if self._tmp_dir != None:
                shutil.rmtree(self._tmp_dir)

    def _is_url(self, url):
        try:
            myurl = urllib.request.urlopen(url).geturl()
        except: 
            return False
        return True

 
    def _get_file_format(self, fp):
        return os.path.splitext(fp)[1][1:]


    def _get_file_name(self, fp):
        return os.path.basename(fp)


    def _md5sum(self, fp):
        md5 = hashlib.md5()
        with open(fp, 'rb') as f:
            for chunk in iter(lambda: f.read(128 * md5.block_size), b''):
                md5.update(chunk)
        return md5.hexdigest()

    def enable_http_auth(self, http_user, http_pass):
        """Use HTTP Authentication for all API calls.
           
           **_http_user_** is the HTTP Authentication username, while **_http_pass_** is the corresponding user password.
        """
        self._http_user = http_user
        self._http_pass = http_pass
        self._http_auth_enabled = True


    def disable_http_auth(self):
        """Disable HTTP Authentication for all API calls"""
        self._http_user = None
        self._http_pass = None
        self._http_auth_enabled = False


    def gen_request_key(self, oid, expire, author=None, author_conf=None):
        """Returns a valid request key to be used as an API authentication token.

	   **_oid_** is the object ID that will be queried (*id* API call
           parameter), and **_expire_** is the UNIX timestamp of the expiration time of
           the key. With these two parameters is generated the *basic request key*.
   
           If **_author_** (*author_id* API parameter) is supplied, then the *full request key* is generated 
           using this parameter plus **_author_conf_** (*author_conf* API parameter).

        """
        s1 = hashlib.sha1()
        s1.update(oid.encode("utf-8") + str(expire).encode("utf-8") + self._api_user.encode('utf-8') + self._api_secret_key.encode("utf-8"))
        rk = s1.hexdigest()
        if author != None:
          s2 = hashlib.sha1()
          s2.update(author.encode('utf-8') + str(author_conf).encode("utf-8") + str(expire).encode("utf-8") + self._api_user.encode('utf-8') + self._api_secret_key.encode("utf-8"))
          rk += s2.hexdigest()
        return rk


    def get_printable_response_data(self):
         """Returns a printable, user friendly version of the response data from the 
            last API call (stored in the `TLPClient.ret_data` class member).
         """
         if self.ret_data == None:
            raise TLPException("No response data available (have you called any API interface before?).")
         if self.ret_content_type.find("application/json") >= 0:
             return json.dumps(self.ret_data, indent=4, ensure_ascii=False)
         else:
             return self.ret_data


    def get_printable_manifest(self):
         """Returns a printable, user friendly version of the manifest JSON object (stored in the `TLPClient.manifest` class member).
         """
         if self.manifest != None:
             return json.dumps(self.manifest, indent=4)
         else:
             raise TLPException("Manifest file was not initialized.")


    def save_response_data(self, dest):
         """Saves the response data from the last API call (stored in the `TLPClient.ret_data` 
            class member) into the destination **_dest_** file.
         """
         with open(dest,'wb') as fd:
             fd.write(self.get_printable_response_data())
 

    def api_systems(self, su=None):
      """ Performs an API call to the /systems interface:

	  > *Get a list of all available Speech Recognition, Machine
	  > Translation, and Text-To-Speech Systems that can be applied to
	  > transcribe, translate, and synthesize a media file.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("systems")
  

class TLPStandardClient(TLPClient):

    def __init__(self, api_base_url, player_base_url, api_user, api_secret_key, api_type):
        """
        Creates a `TLPStandardClient` class instance. Parameters:

        - **_api_base_url_**: Base URL to the transLectures-UPV Platform's API.
        - **_editor_base_url_**: Base URL to the transLectures-UPV Platform's Editor.
        - **_api_user_**: API username / TLP username.
        - **_api_secret_key_**: API Secret Key.
        - **_api_type_**: API type, must be `TLPClient.API_SPEECH`, `TLPClient.API_TEXT` or `TLPClient.API_WEB`.
        """
        self._api_mod_session_id = None
        """Tells wether the /ingest API call requires to upload a media package"""
        self._manifest_files = None
        self.requires_package = False
        self.manifest = None
        """Manifest JSON object."""
        TLPClient.__init__(self, api_base_url, player_base_url, api_user, api_secret_key, api_type)

    def create_media_package(self, dest=None):
        """Creates a Media Package File from the `TLPStandardClient.manifest` JSON object information, returning the location of the zip package.

	   By default the media package file is created and stored in a
           temporal directory, unless you specify the destination file path with the
           optional parameter **_dest_**.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        self._tmp_dir = tempfile.mkdtemp()
        if dest == None:
            dest = "%s/mpf.zip" % self._tmp_dir
        zf = zipfile.ZipFile(dest, mode='w', allowZip64=True)
        for f in self._manifest_files:
            zf.write(f[0], arcname=f[1])
        zf.close()
        return dest

    def api_ingest_new(self, media_package_file=None, debug_mode=False, su=None):
      """ Performs an API call to the /ingest/new interface: 

	  > *Upload media (audio/video) files and many other attachments and
	  > metadata to the TLP Server for automatic multilingual subtitling
	  > and speech synthesization.*

	  If **_media_package_file_** is not provided, then it is generated
          automatically by calling the `TLPStandardClient.create_media_package` method. In
          this latter case, it is assumed that at least a manifest file has been
          initialised before with the `TLPStandardClient.manifest_init` method.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if self.requires_package:
          if media_package_file != None:
              mp_fp = media_package_file
          else:
              mp_fp = self.create_media_package()
      else:
          mp_fp = None
      self.parameters['debug'] = debug_mode
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call_ingest('ingest/new', mp_fp)
      self._last_api_call = "ingest"
   

    def api_ingest_update(self, media_package_file=None, debug_mode=False, su=None):
      """ Performs an API call to the /ingest/update interface: 

	  > *Upload media (audio/video) files and many other attachments and
	  > metadata to the TLP Server for automatic multilingual subtitling
	  > and speech synthesization.*

	  If **_media_package_file_** is not provided, then it is generated
          automatically by calling the `TLPStandardClient.create_media_package` method. In
          this latter case, it is assumed that at least a manifest file has been
          initialised before with the `TLPStandardClient.manifest_init` method.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if self.requires_package:
          if media_package_file != None:
              mp_fp = media_package_file
          else:
              mp_fp = self.create_media_package()
      else:
          mp_fp = None
      self.parameters['debug'] = debug_mode
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call_ingest('ingest/update', mp_fp)
      self._last_api_call = "ingest"
    

    def api_ingest_delete(self, oid, mode=None, debug_mode=False, su=None):
      """ Performs an API call to the /ingest/delete interface: 

	  > *Delete a media object in the TLP Server.*

          **_oid_** must be a media ID.

          Optionally, **_mode__** provides the deletion mode. 

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['debug'] = debug_mode
      if mode != None:
          if mode not in ['hard', 'soft']:
              raise TLPException("Delete mode must be 'hard' or 'soft'")
          self.parameters['mode'] = mode
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call('ingest/delete')
    

    def api_ingest_cancel(self, oid, debug_mode=False, su=None):
      """ Performs an API call to the /ingest/cancel interface: 

	  > * Cancel upload operation. *

          **_oid_** must be an upload ID.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['debug'] = debug_mode
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call('ingest/cancel')
      self._last_api_call = "ingest"

    def api_uploadslist(self, object_id=None, su=None):
      """ Performs an API call to the /uploadslist interface:

          > *Get a list of all user's uploads.*

	  If the optional **_object_id_** argument is given, then the Web
          Service will return a list of uploads involving the provided object ID (could
          be an Upload ID or a Media ID).

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if object_id != None: self.parameters['object_id'] = object_id
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("uploadslist")
      
 
    def api_status(self, oid, su=None):
      """ Performs an API call to the /status interface:

          > *Check the current status of a specific upload ID.*

          **_oid_** is an upload ID, returned by the /ingest interface.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("status")
 
    def api_list(self, su=None):
      """ Performs an API call to the /list interface:

	  > *Get a list of all existing audios and videos in the TLP Database.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("list")
   


    def api_langs(self, oid, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /langs interface:

          > *Get a list of all subtitle and audiotrack languages available for a given media ID.*
      
          The **_oid_** parameter is the Media ID. 

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("langs")
     
    def api_metadata(self, oid, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /metadata interface:

          > *Get metadata and media file locations for a given media ID.*

          The **_oid_** parameter is the Media ID. 

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("metadata")
      
 
    def api_start_session(self, oid, author_id, author_conf, author_name=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /start_session interface:

          > *Starts an edition session to send and commit modifications of a subtitles file.*

          The **_oid_** parameter is the Media ID, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      if author_name != None:  self.parameters['author_name'] = author_name
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("start_session")
      if self.ret_data['rcode'] == 0:
          self._api_mod_session_id = self.ret_data['session_id']
 
      
    def api_session_status(self, oid, author_id, author_conf, session_id=None, alive=False, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /session_status interface:

          > *Returns the current status of the given session ID. If it is alive, it updates the last alive timestamp. This interface is commonly used to avoid the automatic end of session due to user inactivity.*

	  By default, the session ID will be the one returned after calling the
          */start_session* (`TLPStandardClient.start_session`) interface. However, an alternative session ID can be
	  provided via the optional **_session_id_** argument.

          The **_oid_** parameter is the Media ID, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      if session_id != None:
          self.parameters['session_id'] = session_id
      elif self._api_mod_session_id != None:
          self.parameters['session_id'] = self._api_mod_session_id
      else:
          raise TLPException("You must start an edit session to check session status (see api_start_session()), or to manually provide a session ID (session_id optional argument).")
      self.parameters['alive'] = int(alive)
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("session_status")
 
 
    def api_end_session(self, oid, author_id, author_conf, author_name=None, session_id=None, force=False, regenerate=True, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /end_session interface:

          > *Ends an existing edition session.*

	  By default, the session ID will be the one returned after calling the
          */start_session* (`TLPStandardClient.start_session`) interface. However, an alternative session ID can be
	  provided via the optional **_session_id_** argument.

          The **_oid_** parameter is the Media ID, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.

	  **_force_** option should set to *True* to force the end of a session when
          the given **_author_id_** or API user are not the same that started the given
          **_session_id**.

          The **_regenerate_** option tells to the TLP Server if subtitles and/or synthesized audiotracks will be
          regenerated after ending the session. By default it is set to *True*.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      if session_id != None:
          self.parameters['session_id'] = session_id
      elif self._api_mod_session_id != None:
          self.parameters['session_id'] = self._api_mod_session_id
      else:
          raise TLPException("You must start an edit session to send subtitle modifications (see api_start_session()), or to manually provide a session ID (session_id optional argument).")
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      self.parameters['force'] = int(force)
      self.parameters['regenerate'] = int(regenerate)
      if author_name != None:  self.parameters['author_name'] = author_name
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("end_session")
      if self.ret_data['rcode'] == 0:
          self._api_mod_session_id = None

       
    def api_lock(self, oid, lock, author_id, author_conf, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /lock interface:

          > *Allow/disallow regular users to send subtitles modifications for an specific Media ID.*

          The **_oid_** parameter is the Media ID, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      if not(isinstance(lock, bool)):
          raise TLPException("lock must be a boolean.")
      self._reset()
      self.parameters['id'] = oid
      self.parameters['lock'] = int(lock)
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("lock")

       
    def api_edit_history(self, oid, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /edit_history interface:

          > *Returns a list of all edit sessions carried out over an specific Media ID.*

	  The **_oid_** parameter is the Media ID. Optionally, a media hash
          value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("edit_history")
  
    def api_mark_revised(self, oid, session_id, author_id, author_conf, author_name=None, revision_session_id=None, unmark=False, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /mark_revised interface:

          > *Mark/unmark an edition session as revised.*

	  **_session_id_** is the Session ID to mark/unmark as revised,
          the **_oid_** parameter is the Media ID, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.
 
          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      if not(isinstance(unmark, bool)):
          raise TLPException("unmark must be a boolean.")
      self._reset()
      self.parameters['id'] = oid
      self.parameters['session_id'] = session_id
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      self.parameters['unmark'] = int(unmark)
      if author_name != None:  self.parameters['author_name'] = author_name
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("mark_revised")
 
    def api_accept(self, ids, author_id, author_conf, author_name=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /accept interface:

          > *Accept modifications of one or more pending edit sessions without having to revise them.*

	  
	  The **_ids_** parameter must be a list Session IDs whose edits are
          meant to be accepted by the given Author ID.  **_author_id_** is the ID of the
          user that will edit the subtitles (typically the internal user ID that the API
          client's organisation assigns to the user), and **_author_conf_** is an integer
          value (range 0-100) that indicates the confidence level that the API client's
          organisation provide to the user.
 
          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      if not(isinstance(ids, list)):
          raise TLPException("ids must be a list.")
      self._reset()
      self.parameters['id'] = ",".join([ str(x) for x in ids ])
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      if author_name != None:  self.parameters['author_name'] = author_name
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("accept")
 
    def api_reject(self, ids, author_id, author_conf, author_name=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /reject interface:

          > *Reject modifications of one or more pending edit sessions without having to revise them.*

	  
	  The **_ids_** parameter must be a list Session IDs whose edits are
          meant to be rejected by the given Author ID.  **_author_id_** is the ID of the
          user that will edit the subtitles (typically the internal user ID that the API
          client's organisation assigns to the user), and **_author_conf_** is an integer
          value (range 0-100) that indicates the confidence level that the API client's
          organisation provide to the user.
 
          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      if not(isinstance(ids, list)):
          raise TLPException("ids must be a list.")
      self._reset()
      self.parameters['id'] = ",".join([ str(x) for x in ids ])
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      if author_name != None:  self.parameters['author_name'] = author_name
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      else:
          self._set_auth_parameters_secret_key()
      self._call("reject")

    def api_revisions(self, su=None):
      """ Performs an API call to the /revisions interface:

          > *Returns a list of all edit sessions for all API user's media files that are pending to be revised.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("revisions")
 


class TLPSpeechClient(TLPStandardClient):

    FTYPE_CODE_MEDIA = 0
    """Main Media file type code"""
    FTYPE_CODE_SLIDES = 1
    """Slides file type code code"""
    FTYPE_CODE_DOCUMENT = 2
    """Related Text Document file type code"""
    FTYPE_CODE_THUMBNAIL = 3
    """Media thumbnail file type code"""
    FTYPE_CODE_SUBS = 4
    """Subtitles file type code"""
    FTYPE_CODE_AUDIOTRACK = 5
    """Audiotrack file type code"""
    FTYPE_CODE_WAVEFORM = 6
    """Waveform file type code"""
    FTYPE_CODE_NONTIMED_TRANSCRIPT = 7
    """Non-timed transcript file type code"""

    SUBS_SELDATA_ALL = 0
    """Subtitle contents to be returned: Return both former and current contents (only for DFXP format). """
    SUBS_SELDATA_FORMER = 1
    """Subtitle contents to be returned: Return former contents (automatic transcription or translation). """
    SUBS_SELDATA_CURRENT = 2
    """Subtitle contents to be returned: Return current contents (current status of supervision of the subtitles). """
    
    SUBS_SEGFILT_EMTPY = -1
    """Segment text filtering policy: Empty all segments (removes text from all segments). """
    SUBS_SEGFILT_DISABLE = 0
    """Segment text filtering policy: Filtering disabled. """
    SUBS_SEGFILT_ENABLE = 1
    """Segment text filtering policy: Filters out special annotations. """

    DEL_MODE_SOFT = 'soft'
    """Soft delete mode for the /ingest/delete interface"""
    DEL_MODE_HARD = 'hard'
    """Hard delete mode for the /ingest/delete interface"""

    def __init__(self, api_base_url, player_base_url, api_user, api_secret_key):
        """
        Creates a TLPSpeechClient class instance. Parameters:

        - **_api_base_url_**: Base URL to the transLectures-UPV Platform's API.
        - **_player_base_url_**: Base URL to the transLectures-UPV Platform's Player.
        - **_api_user_**: API username / TLP username.
        - **_api_secret_key_**: API Secret Key.
        """
        TLPStandardClient.__init__(self, api_base_url, player_base_url, api_user, api_secret_key, TLPClient.API_SPEECH)

    def manifest_init(self, external_id):
        """Initalize the `TLPSpeechClient.manifest` JSON object.

           **_external_id_** must be a Media ID or an existing upload ID.

           **Note:** This method should be executed before calling any *manifest_** class methods.
        """
        self._tmp_dir = None
        self._manifest_files = []
        self.requires_package = False
        self.manifest = {}
        self.manifest['metadata'] = {}
        self.manifest['metadata']['external_id'] = external_id


    def manifest_set_metadata(self, language=None, title=None, topic=None, keywords=None, date=None):
        """Set metadata in the `TLPSpeechClient.manifest` JSON object.

	   **_language_** is the Media language code in ISO 639-1 format (e.g.
           "en", "es"), **_title_** is the title of the media, **_topic_** is the topic of
           the media, **_keywords_** are the media keywords, and **_date_** is the
           publication date of the media. 

	   All arguments are optional, but **_language_** and **_title_**
           arguments are mandatory for "New" operations.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.

        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        if language != None:
            self.manifest['metadata']['language'] = language
        if title != None:
            self.manifest['metadata']['title'] = title
        if topic != None:
            self.manifest['metadata']['topic'] = topic
        if keywords != None:
            self.manifest['metadata']['keywords'] = keywords
        if date != None:
            self.manifest['metadata']['date'] = date
        
   
    def manifest_add_speaker(self, speaker_id, speaker_name, email=None, gender=None):
        """Add a speaker in the metadata section of the `TLPSpeechClient.manifest` JSON object.

	   **_speaker_id_** is the speaker ID of the media file, **_speaker_name_** is
           the full name of the speaker, **_email_** is the e-mail of the speaker, and
           **_gender_** is the gender of the speaker (*M* for Male or *F* for Female).
           **_email_** and **_gender_** are optional.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        spk = {}
        spk['speaker_id'] = speaker_id
        spk['speaker_name'] = speaker_name
        if gender != None: spk['speaker_gender'] = gender
        if email != None: spk['speaker_email'] = email
        try:
            self.manifest['metadata']['speakers'].append(spk)
        except:
            self.manifest['metadata']['speakers'] = [spk]


    def manifest_set_main_media_file(self, uri, slides=False):
        """Set main media file in the `TLPSpeechClient.manifest` JSON object.

           **_uri_** can be a media file path o a media URL.
           
	   If the **_slides_** flat is set to true, video is treated also as a
           slides file to improve the transcription quality.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        self.manifest['media'] = {}
        if self._is_url(uri):
            self.manifest['media']['url'] = uri
        else:
            self.manifest['media']['fileformat'] = self._get_file_format(uri)
            self.manifest['media']['md5'] = self._md5sum(uri)
            self.manifest['media']['filename'] = "%s.%s" % (self.manifest['media']['md5'], self.manifest['media']['fileformat'])
            #self.manifest['media']['filename'] = self._get_file_name(uri)
            self._manifest_files.append((uri, self.manifest['media']['filename']))
            self.requires_package = True
        self.manifest['media']['slides'] = slides

            
    def manifest_add_attachment(self, uri, file_type_code, language=None, human=None):
        """Add attachment file to the `TLPSpeechClient.manifest` JSON object.

	   **_uri_** is the attachment file path, and **_file_type_code_** must be one of
           the following values: `TLPSpeechClient.FTYPE_CODE_MEDIA`,
           `TLPSpeechClient.FTYPE_CODE_SLIDES`, `TLPSpeechClient.FTYPE_CODE_DOCUMENT`,
           `TLPSpeechClient.FTYPE_CODE_THUMBNAIL`, `TLPSpeechClient.FTYPE_CODE_SUBS`,
           `TLPSpeechClient.FTYPE_CODE_AUDIOTRACK`,
           `TLPSpeechClient.FTYPE_CODE_WAVEFORM`, `TLPSpeechClient.FTYPE_CODE_NONTIMED_TRANSCRIPT`.

	   In case **_file_type_code_** = `TLPSpeechClient.FTYPE_CODE_SUBS`
           or **_file_type_code_** = `TLPSpeechClient.FTYPE_CODE_AUDIOTRACK`, **_language_** and **_human_**
           arguments are mandatory. The first one is the  requested subtitles language
           code in ISO 639-1 format (e.g.  "en", "es"), while **_human_** is a boolean
           variable indicationg whether the provided subtitles or audiotrack have been
           produced by a human, or have been generated automatically by a computer.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        at = {}
        at['fileformat'] = self._get_file_format(uri)
        at['md5'] = self._md5sum(uri)
        #at['filename'] = self._get_file_name(uri)
        at['filename'] = "%s.%s" % (at['md5'], at['fileformat'])
        at['type_code'] = file_type_code
        if language != None: at['language'] = language
        if human != None: at['human'] = human
        try:
            self.manifest['attachments'].append(at)
        except:
            self.manifest['attachments'] = [at]
        self._manifest_files.append((uri, at['filename']))
        self.requires_package = True

   
    def manifest_add_subtitles_request(self, language, system_id=None, lm_adapt=None, tl_path=None):
        """Request subtitles language in the "requested_langs" option of the `TLPSpeechClient.manifest` JSON object.

           **_language_** is the requested subtitles language code in ISO 639-1 format (e.g.
           "en", "es").

	   Some advanced options can be provided to the Ingest Service for
           subtitles generation. **_system_id_** must be an integer value indicating the
           System ID to use, **_lm_adapt_** is a boolean variable to enable or disable the
           Language Model Adaptation technique, and **_tl_path_** must be a list of tuples
           (language, system_id) describing an alternative translation path. In this
           latter case, system_id is optional and must be set to None if is not provided. 

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        if 'requested_langs' not in self.manifest:
            self.manifest['requested_langs'] = {}
        if language not in self.manifest['requested_langs']:
            self.manifest['requested_langs'][language] = {}
        d = {}
        if tl_path != None:
            p = []
            for s in tl_path: # is a tuple (langcode, systemId=None)
                ds = {}
                ds['l'] = s[0]
                if s[1] != None: ds['sid'] = s[1]
                p.append(ds)
            d['tlpath'] = p
        else:
            if system_id != None: d['sid'] = system_id
            if lm_adapt != None: d['lma'] = lm_adapt
        self.manifest['requested_langs'][language]['sub'] = d 


    def manifest_add_audiotrack_request(self, language, system_id=None):
        """Request audiotrack language in the "requested_langs" option of the `TLPSpeechClient.manifest` JSON object.

           **_language_** is the requested audiotrack language code in ISO 639-1 format (e.g.
           "en", "es").

	   Some advanced options can be provided to the Ingest Service for
           subtitles generation. **_system_id_** must be an integer value indicating the
           System ID to use.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        if 'requested_langs' not in self.manifest:
            self.manifest['requested_langs'] = {}
        if language not in self.manifest['requested_langs']:
            self.manifest['requested_langs'][language] = {}
        d = {}
        if system_id != None: d['sid'] = system_id
        self.manifest['requested_langs'][language]['tts'] = d
        

    def manifest_set_options(self, generate=True, regenerate=None, force=False, test_mode=False, email=None):
        """Set Ingest Service options to the `TLPSpeechClient.manifest` JSON object.

           **_generate_** is a boolean variable to enable/disable transcription and translation technologies.

	   On Update operations, **_regenerate_** option can be used to request a regeneration of
           transcriptions, translations and/or synthesized audiotracks.  Must be a list of
           keywords. Allowed Keywords are: *tx* (request regeneration of the media
           transcription), *tl* (request regeneration of media translations), *tts*
           (request regeneration of synthesized audiotracks). Also, if **_force_** = *True*, 
           regeneration of automatic subtitles is forced even if there exist human-supervised subtitles.

           Finally, the **_test_mode_** argument is a boolean variable to enable/disable the Test Mode of the Ingest Service. 
           When enabled, the uploaded media will be available inmediately with an empty subtitles file. 
           This feature is very useful when executing integration tests with the /ingest interface. By default it is disabled.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        self.manifest['generate'] = generate
        self.manifest['force'] = force
        self.manifest['test_mode'] = test_mode
        if regenerate != None: self.manifest['re-generate'] = regenerate
        if email != None: self.manifest['email'] = email.split(",")


    def manifest_add_callback_function(self, url, params=None):
        """Add a HTTP REST callback function into the `TLPSpeechClient.manifest` JSON object, to 
           receive notifications about relevant upload status updates 

           **_url_** is the URL of a HTTP POST function that will receive notifications.

	   **_params_** is an optional dictionary that provides predefined parameter names and values that
           will be added to the POST call (typically, authentication parameters). E.g. params={"auth_token":"deQkw3"}.

	   **Note:** The `TLPSpeechClient.manifest` object should have been
           initialised before with a `TLPSpeechClient.manifest_init` call. This method
           should be executed before calling the `TLPSpeechClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        self.manifest['callback'] = {}
        self.manifest['callback']["url"] = url
        if params != None: 
            if not(isinstance(params, dict)):
                raise TLPException("'params' parameter should be a python dictionary.")
            self.manifest['callback']['params'] = params


    def api_list_fullinfo(self, limit=10, page=1, su=None):
      """ Performs an API call to the /list_fullinfo interface:

	  > *Get a list of all existing audios and videos in the TLP Database, plus
             their metadata, captions available, and media file locations. Objects are
             sorted by ingestion date, from the more recent to the oldest.*

          Queries to this interface are limited to **_limit_** results. Use **_page_** > 1 
          to retrieve further results, if any. 

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['limit'] = limit
      self.parameters['page'] = page
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("list_fullinfo")
 

    def api_get(self, oid, lang, form=None, seldata=None, segfilt=None, session_id=None, pending_revs=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /get interface:

          > *Download current subtitles file for a given media ID and language.*

	  The **_oid_** parameter is the Media ID, and **_lang_** is the
          language code (ISO 639-1) of the requested subtitles language.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

          The **_format_** optional parameter sets the downloaded subtitles format.
          Possible formats are: *dfxp* (default), *srt*, *ttml*, *vtt*, *text*, *json*, *ibmjson*, *stl*, *docx*.

	  **_seldata_** and **_segfilt_** are also optional parameters
          corresponding to the *sel_data_policy* and *seg_filt_policy* API call
          parameters.  

	  On the one hand, **_seldata_** defines which subtitle contents are returned. Possible
          values are: *`TLPSpeechClient.SUBS_SELDATA_ALL`*,
          *`TLPSpeechClient.SUBS_SELDATA_FORMER`*,
          *`TLPSpeechClient.SUBS_SELDATA_CURRENT`*.

	  On the other hand, **_segfilt_** specifies the segment text filtering
          policy. Possible values are: *`TLPSpeechClient.SUBS_SEGFILT_EMTPY`*,
          *`TLPSpeechClient.SUBS_SEGFILT_DISABLE`*,
          *`TLPSpeechClient.SUBS_SEGFILT_ENABLE`*.

	  If **_session_id_** is provided, will return the result of
          applying to the original subtitle file all confirmed subtitle modifications upon
          the given session ID.

          If **_pending_revs_** is provided, will return the current subtitle file applying those revisions 
          that are still pending to be reviewed. Must be a boolean.
 
          For further details of both API call parameters please check the full TLP API documentation.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      Dformats = { 'dfxp':0, 'ttml':1, 'srt':2, 'vtt':3, 'text':4, 'json':5, 'ibmjson':6, 'stl':7, 'docx':8 }
      self.parameters['id'] = oid
      self.parameters['lang'] = lang
      if form != None: self.parameters['format'] = Dformats[form]
      if seldata != None: self.parameters['sel_data_policy'] = seldata
      if segfilt != None: self.parameters['seg_filt_policy'] = segfilt
      if session_id != None: self.parameters['session_id'] = session_id
      if pending_revs != None: 
          if pending_revs:
              self.parameters['pending_revs'] = 1
          else:
              self.parameters['pending_revs'] = 0
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("get")


    def api_getmod(self, oid, session_id, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /getmod interface:

          > *Get subtitle modifications in JSON format of a given media and session ID.*

	  The **_oid_** parameter is the Media ID, and **_session_id_** is the session ID. 
          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.
 
	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['session_id'] = session_id
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("getmod")


      
    def api_audiotrack(self, oid, lang, aid, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /audiotrack interface:

          > *Download an audiotrack file for a given media ID and language.*

          The **_oid_** parameter is the Media ID, **_lang_** is the
          language code (ISO 639-1) of the requested audiotrack language, and
          **_aid_** is the audiotrack ID (retrieved from the /metadata interface).

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['lang'] = lang
      self.parameters['aid'] = aid
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("audiotrack")


    def api_mod(self, oid, lang, author_id, author_conf, txt_array, del_array, author_name=None, session_id=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1, data=None):
      """ Performs an API call to the /mod interface:

          > *Send and commit subtitle corrections made by a user under an edit session ID.*

	  By default, the session ID will be the one returned after calling the
          `TLPSpeechClient.start_session` interface. However, an alternative session ID can be
	  provided via the optional **_session_id_** argument.

	  The **_oid_** parameter is the Media ID, **_lang_** is the language
          code (ISO 639-1) of the edited subtitles language, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.

          **_txt_array_** is a list of JSON dictionaries, where each dictionary contains the following segment edition information:

 
                         {"sI":<int>, "b":<float>, "e":<float>, "t":<str>}


          **sI** is the segment ID, **b** is the segment start time, **e** is the segment end time, and **t** is the updated segment text.
          **_txt_array_** must be an empty list if no segments where modified.

          **_del_array_** is a list containing the segment IDs (int values) to delete. It must be an empty list if no segments are being deleted.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.

	  Alternatively, you can provide a base64-encoded JSON data parameter
          value with all required /mod parameters. In this case, all mandatory arguments can be
          set to *None*, because they are ignored.
      """
      self._reset()
      if data != None:
          self.parameters = json.loads(base64.b64decode(data))
      else:
          self.parameters['id'] = oid
          if session_id != None:
              self.parameters['session_id'] = session_id
          elif self._api_mod_session_id != None:
              self.parameters['session_id'] = self._api_mod_session_id
          else:
              raise TLPException("You must start an edit session to send subtitle modifications (see api_start_session()), or to manually provide a session ID (session_id optional argument).")
          self.parameters['author_id'] = author_id
          self.parameters['author_conf'] = author_conf
          self.parameters['mods'] = { lang: { 'txt':[], 'del':[] } }
          if author_name != None:  self.parameters['author_name'] = author_name
          if txt_array != None: self.parameters['mods'][lang]['txt'] = txt_array
          if del_array != None: self.parameters['mods'][lang]['del'] = del_array
          if vhash != None: self.parameters['hash'] = vhash
          if su != None: self.parameters['su'] = su
          if self.use_get_method and not(self.use_data_parameter): self.parameters['mods'] = base64.b64encode(json.dumps(self.parameters['mods']))
          if use_req_key:
              self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
          else:
              self._set_auth_parameters_secret_key()
      self._call("mod")

    def api_active_sessions(self, oid, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /speech/active_sessions interface:

          > *Returns a list of active edit sessions involving a given media ID..*
      
          The **_oid_** parameter is the Media ID. 

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP Web Service.
      """
      self._reset()
      self.parameters['id'] = oid
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("active_sessions")

    def generate_player_url(self, oid, author_id, author_conf, session_id=None, req_key_lifetime=1440, language=None, author_name=None, start=None, options=None):
      """ Returns a valid TLP Player URL.

	  **_oid_** is the Media ID, **_author_id_** is the ID of the user that
          will edit the subtitles (typically the internal user ID that the API client's
          organisation assigns to the user), and **_author_conf_** is an integer value
          (range 0-100) that indicates the confidence level that the API client's
          organisation provide to the user. 

          It is possible to open the TLP player in read-only (RO) mode: just
          set **_author_id_** parameter to `None`.

	  Optionally, a **_language_** ISO-639-1 code can be provided to make
          the Player load directly the specified subtitles language (if not provided, the
          Player will load the source language transcriptions). Also, you can set the
          media start time in seconds with the **_start_** parameter.

          The full name of the user that will edit the subtitles can be provided with the parameter **_author_name**.

          A **_session_id_** argument can be provided in order review subtitle modifications made under the provided edit session ID.

	  You can adjust de lifetime duration in minutes of the
          generated URL using the option **_req_key_lifetime_**. By default is set to 24
          hours (1440 minutes).

          **_options_** is an optional dict with valid Player options, to be
          passed as `params` via GET. E.g. `{"mode":"transcriptor",
          "autosave":5}`.

      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      if language != None: self.parameters['lang'] = language
      if author_name != None: self.parameters['author_name'] = author_name
      if session_id != None: self.parameters['session_id'] = session_id
      self._set_auth_parameters_request_key_editor(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      if self.debug: self._generate_debug_info_editor()
      request_parameter = base64.b64encode(json.dumps(self.parameters).encode('utf-8')).decode()
      url = "%s?request=%s" % (self._editor_base_url, request_parameter)
      if options != None:
          if not(isinstance(options, dict)):
              raise TLPException("options parameter must be a python dict.")
          params_parameter = base64.b64encode(json.dumps(options).encode('utf-8')).decode()
          url = "%s&params=%s" % (url, params_parameter)
      if start != None:
        url += "&t=%d" % int(start)
      return url



class TLPTextClient(TLPStandardClient):

    TRANS_FORMAT_ORIGINAL = 0
    """Translation file in original format"""
    TRANS_FORMAT_HTML = 1
    """Translation file in internal HTML format"""
    TRANS_FORMAT_DTLX = 2
    """Translation file in internal DTLX format"""
    TRANS_FORMAT_TEXT = 3
    """Translation file in text format"""

    TRANS_SELDATA_ALL = 0
    """Translation contents to be returned: Return both former and current contents (only for DTLX format). """
    TRANS_SELDATA_FORMER = 1
    """Translation contents to be returned: Return former contents (automatic translation). """
    TRANS_SELDATA_CURRENT = 2
    """Translation contents to be returned: Return current contents (current status of supervision of the translation). """
    
    DEL_MODE_SOFT = 'soft'
    """Soft delete mode for the /ingest/delete interface"""
    DEL_MODE_HARD = 'hard'
    """Hard delete mode for the /ingest/delete interface"""

    def __init__(self, api_base_url, translation_editor_base_url, api_user, api_secret_key):
        """
        Creates a TLPTextClient class instance. Parameters:

        - **_api_base_url_**: Base URL to the transLectures-UPV Platform's API.
        - **_translation_editor_base_url_**: Base URL to the transLectures-UPV Platform's Translation Editor.
        - **_api_user_**: API username / TLP username.
        - **_api_secret_key_**: API Secret Key.
        """
        TLPStandardClient.__init__(self, api_base_url, translation_editor_base_url, api_user, api_secret_key, TLPClient.API_TEXT)

    def _set_auth_parameters_request_key_translation_editor(self, req_key_lifetime, oid, author=None, author_conf=None):
        """Set API authentication parameters using a request key for generating a Player URL."""
        self.parameters['api_user'] = self._api_user
        expire = self._gen_expire_parameter(req_key_lifetime)
        self.parameters['request_key'] = self.gen_request_key(oid, expire, author, author_conf)
        self.parameters['expire'] = expire


    def manifest_init(self, language):
        """Initalize the `TLPTextClient.manifest` JSON object.

           **_language_** is the documents' language code in ISO 639-1 format (e.g.
           "en", "es").

           **Note:** This method should be executed before calling the `TLPTextClient.api_ingest` method and any *manifest_** class methods.
        """
        self._tmp_dir = None
        self._manifest_files = []
        self.requires_package = False
        self.manifest = {}
        self.manifest['documents'] = []
        self.manifest['language'] = language


    def manifest_add_document(self, external_id, uri=None, title=None, fileformat=None):
        """Set main media file in the `TLPTextClient.manifest` JSON object.

           **_external_id_** must be a Document ID.
 
           **_uri_** can be a media file path o a media URL.
 
           **_title_** is the title of the media.

	   **Note:** The `TLPTextClient.manifest` object should have been
           initialised before with a `TLPTextClient.manifest_init` call. This method
           should be executed before calling the `TLPTextClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        doc = {}
        doc['external_id'] = external_id
        if title != None:
            doc['title'] = title
        if uri != None:
            if self._is_url(uri):
                doc['url'] = uri
            else:
                if fileformat != None:
                  doc['fileformat'] = fileformat
                else:
                  doc['fileformat'] = self._get_file_format(uri)
                doc['md5'] = self._md5sum(uri)
                doc['filename'] = "%s.%s" % (doc['md5'], doc['fileformat'])
                #doc['filename'] = self._get_file_name(uri)
                self._manifest_files.append((uri, doc['filename']))
                self.requires_package = True
        self.manifest['documents'].append(doc)

            
    def manifest_add_translation_request(self, language, system_id=None, tl_path=None):
        """Request subtitles language in the "requested_langs" option of the `TLPTextClient.manifest` JSON object.

           **_language_** is the requested subtitles language code in ISO 639-1 format (e.g.
           "en", "es").

	   Some advanced options can be provided to the Ingest Service for
           subtitles generation. **_system_id_** must be an integer value indicating the
           System ID to use, and **_tl_path_** must be a list of tuples
           (language, system_id) describing an alternative translation path. In this
           latter case, system_id is optional and must be set to None if is not provided. 

	   **Note:** The `TLPTextClient.manifest` object should have been
           initialised before with a `TLPTextClient.manifest_init` call. This method
           should be executed before calling the `TLPTextClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        if 'requested_langs' not in self.manifest:
            self.manifest['requested_langs'] = {}
        if language not in self.manifest['requested_langs']:
            self.manifest['requested_langs'][language] = {}
        d = {}
        if tl_path != None:
            p = []
            for s in tl_path: # is a tuple (langcode, systemId=None)
                ds = {}
                ds['l'] = s[0]
                if s[1] != None: ds['sid'] = s[1]
                p.append(ds)
            d['tlpath'] = p
        else:
            if system_id != None: d['sid'] = system_id
        self.manifest['requested_langs'][language] = d 


    def manifest_set_options(self, generate=True, regenerate=False, force=False, test_mode=False, email=None):
        """Set Ingest Service options to the `TLPTextClient.manifest` JSON object.

           **_generate_** is a boolean variable to enable/disable transcription and translation technologies.

	   On Update operations, **_regenerate_** option can be used to request a regeneration of
           translations. Also, if **_force_** = *True*, 
           regeneration of automatic subtitles is forced even if there exist human-supervised translations.

           Finally, the **_test_mode_** argument is a boolean variable to enable/disable the Test Mode of the Ingest Service. 
           When enabled, the uploaded media will be available inmediately with an empty translation file. 
           This feature is very useful when executing integration tests with the /ingest interface. By default it is disabled.

	   **Note:** The `TLPTextClient.manifest` object should have been
           initialised before with a `TLPTextClient.manifest_init` call. This method
           should be executed before calling the `TLPTextClient.api_ingest` method.
        """
        if self.manifest == None:
            raise TLPException("Manifest file was not initialized.")
        self.manifest['generate'] = generate
        self.manifest['force'] = force
        self.manifest['test_mode'] = test_mode
        if regenerate != None: self.manifest['re-generate'] = regenerate
        if email != None: self.manifest['email'] = email.split(",")


    def api_get(self, oid, lang, form=None, seldata=None, session_id=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /get interface:

          > *Download current subtitles file for a given media ID and language.*

	  The **_oid_** parameter is the Media ID, and **_lang_** is the
          language code (ISO 639-1) of the requested subtitles language.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

          The **_format_** optional parameter sets the downloaded subtitles format.
          Possible values are:  *`TLPTextClient.TRANS_FORMAT_ORIGINAL`*, 
          *`TLPTextClient.TRANS_FORMAT_HTML`*,
          *`TLPTextClient.TRANS_FORMAT_DTLX`*,
          *`TLPTextClient.TRANS_FORMAT_TXT`*.

	  **_seldata_** defines which subtitle contents are returned. Possible
          values are: *`TLPTextClient.TRANS_SELDATA_ALL`*,
          *`TLPTextClient.TRANS_SELDATA_FORMER`*,
          *`TLPTextClient.TRANS_SELDATA_CURRENT`*.

          A **_session_id_** argument can be provided in order to apply subtitle modifications made under the given edit session ID to the subtitles file.
 
          For further details of both API call parameters please check the full TLP API documentation.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['lang'] = lang
      if form != None: 
          if form not in (self.TRANS_FORMAT_ORIGINAL, self.TRANS_FORMAT_HTML, self.TRANS_FORMAT_DTLX, self.TRANS_FORMAT_TEXT):
              raise TLPException("Unallowed document format.")
          self.parameters['format'] = form
      if seldata != None: self.parameters['sel_data_policy'] = seldata
      if session_id != None: self.parameters['session_id'] = session_id
      if vhash != None: self.parameters['hash'] = vhash
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, oid)
      else:
          self._set_auth_parameters_secret_key()
      self._call("get")

      
    def api_mod(self, oid, lang, author_id, author_conf, txt_array, author_name=None, session_id=None, vhash=None, su=None, use_req_key=False, req_key_lifetime=1, data=None):
      """ Performs an API call to the /mod interface:

          > *Send and commit subtitle corrections made by a user under an edit session ID.*

	  By default, the session ID will be the one returned after calling the
          `TLPTextClient.start_session` interface. However, an alternative session ID can be
	  provided via the optional **_session_id_** argument.

	  The **_oid_** parameter is the Media ID, **_lang_** is the language
          code (ISO 639-1) of the edited subtitles language, **_author_id_** is the ID of
          the user that will edit the subtitles (typically the internal user ID that the
          API client's organisation assigns to the user), and **_author_conf_** is an
          integer value (range 0-100) that indicates the confidence level that the API
          client's organisation provide to the user.

          **_txt_array_** is a list of JSON dictionaries, where each dictionary contains the following segment edition information:

 
                         {"sI":<int>, "t":<str>}


          **sI** is the segment ID, and **t** is the updated segment text.
          **_txt_array_** must be an empty list if no segments where modified.

          Optionally, a media hash value can be provided using the optional **_vhash_** parameter.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.

	  Alternatively, you can provide a base64-encoded JSON data parameter
          value with all required /mod parameters. In this case, all mandatory arguments can be
          set to *None*, because they are ignored.
      """
      self._reset()
      if data != None:
          self.parameters = json.loads(base64.b64decode(data))
      else:
          self.parameters['id'] = oid
          if session_id != None:
              self.parameters['session_id'] = session_id
          elif self._api_mod_session_id != None:
              self.parameters['session_id'] = self._api_mod_session_id
          else:
              raise TLPException("You must start an edit session to send subtitle modifications (see api_start_session()), or to manually provide a session ID (session_id optional argument).")
          self.parameters['author_id'] = author_id
          self.parameters['author_conf'] = author_conf
          self.parameters['mods'] = { lang: { 'txt':[] } }
          if author_name != None:  self.parameters['author_name'] = author_name
          if txt_array != None: self.parameters['mods'][lang]['txt'] = txt_array
          if vhash != None: self.parameters['hash'] = vhash
          if su != None: self.parameters['su'] = su
          if self.use_get_method and not(self.use_data_parameter): self.parameters['mods'] = base64.b64encode(json.dumps(self.parameters['mods']))
          if use_req_key:
              self._set_auth_parameters_request_key_api(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
          else:
              self._set_auth_parameters_secret_key()
      self._call("mod")

 
    def generate_translation_editor_url(self, oid, language, author_id, author_conf, session_id=None, req_key_lifetime=1440, author_name=None):
      """ Returns a valid TLP Translation Editor URL.

	  **_oid_** is the Document ID, **_language_** is the ISO-639-1 code of the translation language to edit, 
          **_author_id_** is the ID of the user that
          will edit the subtitles (typically the internal user ID that the API client's
          organisation assigns to the user), and **_author_conf_** is an integer value
          (range 0-100) that indicates the confidence level that the API client's
          organisation provide to the user. 

          The full name of the user that will edit the subtitles can be provided with the parameter **_author_name**.

          A **_session_id_** argument can be provided in order review subtitle modifications made under the provided edit session ID.

	  Finally, you can adjust de lifetime duration in minutes of the
          generated URL using the option **_req_key_lifetime_**. By default is set to 24
          hours (1440 minutes).
      """
      self._reset()
      self.parameters['id'] = oid
      self.parameters['author_id'] = author_id
      self.parameters['author_conf'] = author_conf
      self.parameters['lang'] = language
      if author_name != None: self.parameters['author_name'] = author_name
      if session_id != None: self.parameters['session_id'] = session_id
      self._set_auth_parameters_request_key_editor(req_key_lifetime, oid, author=author_id, author_conf=author_conf)
      if self.debug: self._generate_debug_info_editor()
      request_parameter = base64.b64encode(json.dumps(self.parameters).encode('utf-8')).decode()
      url = "%s?request=%s" % (self._editor_base_url, request_parameter)
      return url


class TLPWebClient(TLPClient):

    def __init__(self, api_base_url, web_translation_editor_base_url, api_user, api_secret_key):
        """
        Creates a TLPWebClient class instance. Parameters:

        - **_api_base_url_**: Base URL to the transLectures-UPV Platform's API.
        - **_api_user_**: API username / TLP username.
        - **_api_secret_key_**: API Secret Key.
        """
        TLPClient.__init__(self, api_base_url, web_translation_editor_base_url, api_user, api_secret_key, TLPClient.API_WEB)

    def api_manage_list(self, su=None):
      """ Performs an API call to the /web/manage/list interface:

          > *Get list of websites.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("manage/list")

    def api_manage_add(self, name, language, url_pattern, requested_langs, su=None):
      """ Performs an API call to the /web/manage/add interface:

          > *Adds a new web site.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['name'] = name
      self.parameters['language'] = language
      self.parameters['url_pattern'] = url_pattern
      self.parameters['requested_langs'] = requested_langs
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("manage/add")

    def api_manage_edit(self, key, enabled=None, url_pattern=None, requested_langs=None, su=None):
      """ Performs an API call to the /web/manage/edit interface:

          > *Edits and existing web site.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = key
      if enabled != None: self.parameters['enabled'] = enabled
      if url_pattern != None: self.parameters['url_pattern'] = url_pattern
      if requested_langs != None: self.parameters['requested_langs'] = requested_langs
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("manage/edit")

    def api_manage_delete(self, key, purge=None, su=None):
      """ Performs an API call to the /web/manage/delete interface:

          > *Deletes an existing web site.*

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = key
      if purge != None: self.parameters['purge'] = purge
      if su != None: self.parameters['su'] = su
      self._set_auth_parameters_secret_key()
      self._call("manage/delete")

    def api_editor_langs(self, key, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /web/editor/langs interface:

          > *Returns a list of translation languages available for a specific website.*

          **_key_** must be the Website Key.

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = key
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, key)
      else:
          self._set_auth_parameters_secret_key()
      self._call("editor/langs")

    def api_editor_get(self, key, language, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /web/editor/get interface:

          > *Returns all existing source texts or translations of a specific website.*

          **_key_** must be the Website Key, and **_language_** the requested language code in ISO 639-1 format (e.g.
           "en", "es"). 

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = key
      self.parameters['language'] = language
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, key)
      else:
          self._set_auth_parameters_secret_key()
      self._call("editor/get")

    def api_editor_mod(self, key, language, texts, su=None, use_req_key=False, req_key_lifetime=1):
      """ Performs an API call to the /web/editor/mod interface:

          > *Updates text translations of a specific website and language.*

          **_key_** must be the Website Key, **_language_** the requested language code in ISO 639-1 format (e.g.
           "en", "es"), and **_texts_** a dictionary of text hashes and text contents (i.e. '{"af129cd836a":"My updated text"}').

	  If **_use_req_key_** *= True*, the API call will be performed using a
          valid *Request Key* as authentication token instead of the *Secret Key*. **_req_key_lifetime_**
          defines the *Request Key* lifetime in minutes, by default is 1 minute.

	  The **_su_** (*substitute user*) option can be used by Admin users
          to be identified as another existing API user at the TLP API.
      """
      self._reset()
      self.parameters['id'] = key
      self.parameters['language'] = language
      self.parameters['text'] = texts
      if su != None: self.parameters['su'] = su
      if use_req_key:
          self._set_auth_parameters_request_key_api(req_key_lifetime, key)
      else:
          self._set_auth_parameters_secret_key()
      self._call("editor/mod")

    def generate_web_translation_editor_url(self, web_key, language, req_key_lifetime=1440):
      """ Returns a valid TLP Translation Editor URL.

	  **_web_key_** is the Website Key, and **_language_** is the ISO-639-1 code of the translation language to edit.

	  You can adjust de lifetime duration in minutes of the
          generated URL using the option **_req_key_lifetime_**. By default is set to 24
          hours (1440 minutes).
      """
      self._reset()
      self.parameters['id'] = web_key
      self.parameters['lang'] = language
      self._set_auth_parameters_request_key_editor(req_key_lifetime, web_key)
      if self.debug: self._generate_debug_info_editor()
      request_parameter = base64.b64encode(json.dumps(self.parameters).encode('utf-8')).decode()
      url = "%s?request=%s" % (self._editor_base_url, request_parameter)
      return url



class TLPException(Exception):
    """Generic TLP Exception"""
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg
 
class APIBadRequest(TLPException):
    """Exception that is raised when the TLP API returns a "400 Bad Request" HTTP error code."""

    def __str__(self):
        string = 'TLP API retuned a "400 Bad Request" HTTP error code'
        if self.msg.strip() != "":
            string = '%s: \n\n%s\n' % (string, self.msg)
        else:
            string = '%s.' % string
        return string
 
class APIUnauthorized(TLPException):
    """Exception that is raised when the TLP API returns a "401 Unauthorized" HTTP error code."""

    def __str__(self):
        string = 'TLP API retuned a "401 Unautorized" HTTP error code (please check your API credentials)'
        if self.msg.strip() != "":
            string = '%s: \n\n%s\n' % (string, self.msg)
        else:
            string = '%s.' % string
        return string
  
class APIAuthenticationTimeout(TLPException):
    """Exception that is raised when the TLP API returns a "419 Authentication Timeout" HTTP error code."""

    def __str__(self):
        string = 'TLP API retuned a "419 Authentication Timeout" HTTP error code'
        if self.msg.strip() != "":
            string = '%s: \n\n%s\n' % (string, self.msg)
        else:
            string = '%s.' % string
        return string
   
class APIInternalServerError(TLPException):
    """Exception that is raised when the TLP API returns a "500 Internal Server Error" HTTP error code."""

    def __str__(self):
        string = 'TLP API retuned a "500 Internal Server Error" HTTP error code'
        if self.msg.strip() != "":
            string = '%s: \n\n%s\n' % (string, self.msg)
        else:
            string = '%s.' % string
        return string


