Example #1
0
    def add_count(self, url, params, request, response):
        """ Add user action count of choosen AV.

        [Arguments]
            url[0] : string.
                AV ID.
            type   : string.
                Action type.
        """
        # API args
        av_id = url[0] if len(url) else None
        action_type = params.get_string('type', None)

        if not action_type or action_type not in [
                USER_ACTION_VIEW, USER_ACTION_DOWNLOAD, USER_ACTION_PLAY
        ]:
            raise RequestError(ERR_REQUEST_ARG, str_data=('type error.'))

        db_inst = PGClient(db='av')

        cur, rowcount = db_inst.query(table='video',
                                      condition=['id=%(id)s', {
                                          'id': av_id
                                      }])
        if not rowcount:
            raise RequestError(ERR_DB_NOT_FOUND)

        count, total = self._add_user_action(action_type=action_type,
                                             av_id=av_id,
                                             db_inst=db_inst)

        db_inst.commit()

        return Response(content={'count': count, 'total': total})
Example #2
0
    def execute(self, cmd, data=None):
        """Basic database query method.

        [Arguments]
            cmd: str
                All SQL statment that you can input.

            data: dict (optional)
                Pass data separately with dict.

        [Return]
            cur: cursor instance.
        """
        try:
            if data:
                self.cur.execute(cmd, data)
            else:
                self.cur.execute(cmd)

            return self.cur
        except psycopg2.extensions.QueryCanceledError:
            self.conn.rollback()
            raise RequestError(ERR_DB_TIME_OUT)
        except Exception, e:
            self.conn.rollback()
            # TODO: write log if we need.
            raise RequestError(ERR_DB_QUERY)
Example #3
0
    def delete_addon(self, url, params, request, response):
        """ delete addon file.

        [Arguments]
            url[0] : string.
                Addon version of file to remove.
        """
        # API args
        if len(url):
            version = url[0]
        else:
            raise RequestError(ERR_REQUEST_ARG, str_data=('version error.'))

        db_inst = PGClient(db='file', timeout=False)
        # check data exist
        cur, rowcount = db_inst.query(
            table='addons',
            fields=['version'],
            condition=[u'version=%(version)s', {
                'version': version
            }])
        if not rowcount:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=('data does not exist.'))

        db_inst.remove(
            table='addons',
            condition=[u'version=%(version)s', {
                'version': version
            }])
        db_inst.commit()

        return Response({'version': version})
Example #4
0
def _eval_cast(string):
    if not isinstance(string, basestring):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a string'))
    try:
        return literal_eval(string)
    except:
        raise RequestError(ERR_REQUEST_ARG, str_data=('content error'))
Example #5
0
    def patch_data(self, url, params, request, response):
        """ patch data to db

        create patcher by tablename and execute patch.

        :param url:
        :param params:
        :param request:
        :param response:
        :return:
        """
        tablename = params.get_string('tablename', None)
        # check tablename argument
        if not tablename:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'tablename' is need.", ))

        # check patchfile argument
        if 'patchfile' not in params:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'patchfile' is need.", ))

        # get patcher
        patcher = create_patcher(tablename)
        if not patcher:
            raise RequestError(
                ERR_REQUEST_ARG,
                str_data=("argument 'tablename' is incorrect.", ))
        # execute patch task
        patcher.patch(params['patchfile'], 'patch_data')
        # return message
        return Response(
            model_data={
                'message': "start to patch %s" % patcher.get_tablename()
            })
Example #6
0
    def set_err(self, success=NotSet, err_code=SUCCESS, err_msg=None, err_msg_data=None, http_status=BAD_REQUEST):
        """ Set error information.
        [Arguments]
            See __init__.
        [Notes]
            Compare using a error Response with raising a RequestError, the error Response can include
            more information, but the RequestError only has error information.
        """
        if success is NotSet:
            return self  # nothing change
        elif not isinstance(success, bool):
            raise InternalError(code=ERR_RESPONSE_FORMAT, str_data=('success is not bool'))

        if not isinstance(err_code, int):
            raise InternalError(code=ERR_RESPONSE_FORMAT, str_data=('err_code is not int'))

        if not err_msg:  # auto set err_msg
            err = RequestError(code=err_code, str_data=err_msg_data, http_status=http_status)
            err_msg, http_status = err.get_msg(), err.http_status

        if success:
            self.pop('error', None)  # Success response has no error information.
            self.http_status = OK
        else:
            self.update({
                'error': {  # Not include success flag here because that is not useful.
                    'err_code': err_code,
                    'err_msg': err_msg
                }
            })
            self.http_status = http_status
        return self
Example #7
0
def param2int(string, default=None):
    """ Number string cast to integer.
    """
    if not string:
        return default
    if not isinstance(string, basestring):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a string'))
    if not string.isdigit():
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a number'))
    return int(string)
Example #8
0
def param2float(string, default=None):
    """ Number string cast to float.
    """
    if not string:
        return default
    if not isinstance(string, basestring):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a string'))
    try:
        return float(string)
    except ValueError:
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a float number'))
Example #9
0
    def get_play_list(self, url, params, request, response):
        """ Get play list.

        [Arguments]
            url[0] : string.
                Drama ID.
            source :
                Video source.
            since  : int string.
                Response rows start from.
            limit  : int string.
                Number of response rows to display.
        """
        # API args
        drama_id = url[0] if len(url) else None
        source = params.get_string('source', None)
        since = params.get_int('since', 0)
        limit = params.get_limit('limit', 30)

        if not drama_id:
            raise RequestError(http_status=404)

        condition = ['id=%(id)s', {'id': drama_id}]

        if source in self.allow_display_play_source():
            fields = [
                '''
                (
                    ARRAY(SELECT json_array_elements(play_urls->'%s'))
                )[%s:%s] AS play_list
            ''' % (
                    source,
                    since + 1 if since > 0 else 1,
                    limit + since
                    if limit > 0 else "json_array_length(play_urls->'%s')" %
                    source  # limit it if need.
                )
            ]
        else:
            raise RequestError(ERR_REQUEST_ARG, str_data=('source.'))

        db_inst = PGClient()

        dramas, rowcount = db_inst.query(table='drama',
                                         fields=fields,
                                         condition=condition,
                                         ret_type='all')

        if not rowcount:
            raise RequestError(http_status=404)

        return Response(model_data={'data': dramas[0]})
Example #10
0
    def get_patch_columns(self, url, params, request, response):
        tablename = params.get_string('tablename', None)
        # check tablename argument
        if not tablename:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'tablename' is need.", ))

        # get patcher
        patcher = create_patcher(tablename)
        if not patcher:
            raise RequestError(
                ERR_REQUEST_ARG,
                str_data=("argument 'tablename' is incorrect.", ))
        # return patch columns
        return Response(model_data={'columns': patcher.get_columns()})
Example #11
0
    def get_task_status(self, url, params, request, response):
        """ Get task status.

        [Arguments]
            task: string
                task name.
                'rebuild_movie_keyword'
                'patch_data'
        [Return]
            task_done: string
                task status.
                True is done, False is not yet.
        """
        task = params.get_string('task', None)
        # check argument 'task'
        if not task:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'task' is need.", ))

        taskobj = g().get(task)
        # can't find task
        if not taskobj:
            return Response(success=False, err_msg="can't find task.")
        # some error occur
        if taskobj.error:
            return Response(success=False,
                            err_msg=taskobj.error_message
                            if taskobj.error_message else 'unhandle error')
        # return True: task done; False: not yet
        return Response(model_data={'task_done': taskobj.done})
Example #12
0
    def get_addon_version(self, url, params, request, response):
        """ Get addon version.
        """
        # API args
        version = url[0] if len(url) else None

        db_inst = PGClient(db='file')
        if version:
            addons, rowcount = db_inst.query(
                table='addons',
                fields=['version', 'md5sum'],
                condition=[u'version=%(version)s', {
                    'version': version
                }],
                ret_type='all')
        else:  # latest version
            addons, rowcount = db_inst.query(
                table='addons',
                fields=['version', 'md5sum'],
                ret_type='all',
                else_sql='ORDER BY version DESC LIMIT 1')

        if not rowcount:
            raise RequestError(ERR_DB_NOT_FOUND)

        return Response({
            'version': addons[0]['version'],
            'md5sum': addons[0]['md5sum']
        })
Example #13
0
    def search_media(self, url, params, request, response):
        """ Search multimedia information.

        [Arguments]
            See _parse_args.
        """
        args = self._parse_args(url, params)

        ret = {
            'detail': True,  # FIXME if this filed is not a common data.
            'media_type': args['media_type']
        }

        try:
            search_inst = {
                'movies': search_movies,
                'karaokes': search_karaokes,
                'avs': search_avs,
                'tvs': search_tvs
            }[args.pop('media_type')]
        except:
            raise RequestError(ERR_REQUEST_ARG, str_data=('media type error'))

        search_ret = search_inst(**args)
        ret.update(search_ret)

        return Response(model_data=ret)
Example #14
0
    def get_movie_titles(self, url, params, request, response):
        """ Get movie titles of each source.

        [Arguments]
            url[0] : string.
                IMDB ID.
        """
        # API args
        if not url:
            raise RequestError(ERR_REQUEST_ARG, str_data=('format error.'))
        imdbid = url[0]

        db_inst = PGClient()

        condition = ['imdbid = %(imdbid)s', {'imdbid': imdbid}]
        rows, rowcount = db_inst.query(table='movies',
                                       fields=['source, title'],
                                       condition=condition,
                                       ret_type='all')

        return Response(
            content={
                'titles': {
                    source2lang(row['source']): row['title']
                    for row in rows if source2lang(row['source']) is not None
                }
            })
Example #15
0
    def get_metadata(self, url, params, request, response):
        """ Use TV id of any source to get all metadata.
        [Arguments]
            ids : list string.
                IDs of TVs to get metadata.
        """
        # API args
        ids = params.get_list('ids', None)
        if not ids:
            raise RequestError(ERR_REQUEST_ARG, str_data=('ids.'))

        resp = Response(content={'tvs': {}})

        db_inst = PGClient()

        # Get items with ids
        condition = ['id IN %(id)s', {'id': tuple(ids)}]
        tvs, rowcount = db_inst.query(table='drama',
                                      condition=condition,
                                      ret_type='all')

        for tv in tvs:
            resp['tvs'][tv['id']] = {'dbid': tv['dbid'], 'metadata': {}}

            # Get metadata of each source.
            if tv['dbid']:
                condition = ['dbid = %(id)s', {'id': tv['dbid']}]
                rows, rowcount = db_inst.query(table='drama',
                                               condition=condition,
                                               ret_type='all')
            else:  # only one record
                rows = [tv]

            for row in rows:
                resp['tvs'][tv['id']]['metadata'][row['source']] = {
                    'id': row['id'],
                    'source': row['source'],
                    'title': row['title'],
                    'akas': row['akas'],
                    'kind': row['kind'],
                    'genres': row['genres'],
                    'posterurl': tv['posterurl'],
                    'stars': row['stars'],
                    'year': row['year'],
                    'region': row['region'],
                    'description': row['description'],
                    'url': row['url'],
                    'update_eps': row['update_eps'],
                    'total_eps': row['total_eps'],
                    'completed': row['completed'],
                    'md5sum': row['md5sum']
                }

        # patch up not found data.
        for missing_id in set(ids).difference(resp['tvs'].keys()):
            resp['tvs'][missing_id] = {}

        return resp
Example #16
0
    def get_addon(self, url, params, request, response):
        """ Get addon file.

        [Arguments]
            url[0] : string.
                Addon version.
        """
        # API args
        if len(url):
            version = url[0]
        else:
            raise RequestError(ERR_REQUEST_ARG, str_data=('version error.'))

        # Compose cache file path
        file_name = 'CEC_%s.addon' % version
        cache_file_path = '%s/%s' % (FILE_CACHE_PATH, file_name)

        path_to_save = None  # Save file if this file does not cached.

        # Get file
        if os.path.isfile(cache_file_path):  # Take cached file if it found.
            iter_data = open(cache_file_path)
        else:  # get file and cache it
            db_inst = PGClient(db='file', timeout=False)
            addons, rowcount = db_inst.query(
                table='addons',
                condition=[u'version=%(version)s', {
                    'version': version
                }],
                ret_type='all')
            if not rowcount:
                raise RequestError(ERR_DB_NOT_FOUND)

            iter_data = iter(
                addons[0]
                ['data'])  # covert it to iter obj for avoiding multiple use.
            path_to_save = cache_file_path

        response.stream = True
        response.headers['Content-Type'] = 'application/octet-stream'
        response.headers[
            'Content-Disposition'] = 'attachment; filename="%s"' % file_name

        return streaming_generator(iter_data=iter_data, file_name=path_to_save)
Example #17
0
def param2json(string, default=None, check_type=NotSet):
    """ Json format string cast to dict.
    """
    if not string:
        return default

    if not isinstance(string, basestring):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a string'))
    try:
        ret = loads(string)

        if check_type is not NotSet:
            if not isinstance(ret, check_type):
                raise RequestError(ERR_REQUEST_ARG, str_data=('format error'))
        return ret
    except RequestError:
        raise
    except:
        raise RequestError(ERR_REQUEST_ARG, str_data=('content error'))
Example #18
0
    def set_err(self,
                success=NotSet,
                err_code=SUCCESS,
                err_msg=None,
                err_msg_data=None,
                http_status=BAD_REQUEST):
        """ Set error information.
        [Arguments]
            See __init__.
        [Notes]
            Compare using a error Response with raising a RequestError, the error Response can include
            more information, but the RequestError only has error information.
        """
        if success is NotSet:
            return self  # nothing change
        elif not isinstance(success, bool):
            raise InternalError(code=ERR_RESPONSE_FORMAT,
                                str_data=('success is not bool'))

        if not isinstance(err_code, int):
            raise InternalError(code=ERR_RESPONSE_FORMAT,
                                str_data=('err_code is not int'))

        if not err_msg:  # auto set err_msg
            err = RequestError(code=err_code,
                               str_data=err_msg_data,
                               http_status=http_status)
            err_msg, http_status = err.get_msg(), err.http_status

        if success:
            self.pop('error',
                     None)  # Success response has no error information.
            self.http_status = OK
        else:
            self.update({
                'error':
                {  # Not include success flag here because that is not useful.
                    'err_code': err_code,
                    'err_msg': err_msg
                }
            })
            self.http_status = http_status
        return self
Example #19
0
def param2list(string, default=None):
    """ List format string cast to list.
    """
    if not string:
        return default

    ret = _eval_cast(string)

    if not isinstance(ret, list):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a list'))
    return ret
Example #20
0
def param2dict(string, default=None):
    """ Dictionary format string cast to dict.
    """
    if not string:
        return default

    ret = _eval_cast(string)

    if not isinstance(ret, dict):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a dict'))
    return ret
Example #21
0
    def rebuild_keyword(self, url, params, request, response):
        tablename = params.get_string('tablename', None)
        # check tablename argument
        if not tablename:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'tablename' is need.", ))

        # get builder
        builder = create_keyword_builder(tablename)
        if not builder:
            raise RequestError(
                ERR_REQUEST_ARG,
                str_data=("argument 'tablename' is incorrect.", ))
        # execute rebuild keyword task
        builder.build('rebuild_keyword')
        # return message
        return Response(
            model_data={
                'message': 'start to rebuild %s' % builder.get_tablename()
            })
Example #22
0
    def add_count(self, url, params, request, response):
        """ Add user action count of choosen karaoke.

        [Arguments]
            url[0] : string.
                Karaoke ID.
            type   : string.
                Action type.
        """
        # API args
        karaoke_id = url[0] if len(url) else None
        id_list = params.get_list('ids', [])
        action_type = params.get_string('type', None)

        if not action_type or action_type not in [USER_ACTION_VIEW, USER_ACTION_DOWNLOAD, USER_ACTION_PLAY]:
            raise RequestError(ERR_REQUEST_ARG, str_data=('type error.'))

        if karaoke_id:
            id_list = [karaoke_id]
        elif not id_list:
            raise RequestError(ERR_REQUEST_ARG, str_data=('id_list error.'))

        db_inst = PGClient(db='karaoke')

        cur, rowcount = db_inst.query(table='songs', condition=['keymd5 IN %(ids)s', {'ids': tuple(id_list)}])
        if rowcount != len(id_list):
            raise RequestError(ERR_DB_NOT_FOUND)
   
        resp = Response()

        for ID in id_list:
            count, total = self._add_user_action(action_type=action_type, karaoke_id=ID, db_inst=db_inst)
            resp[ID] = {
                'count': count,
                'total': total
            }

        db_inst.commit()

        return resp.pop(karaoke_id) if karaoke_id else resp
Example #23
0
    def get_covers(self, url, params, request, response):
        """ Get karaoke covers.

        [Arguments]
            song_langs : list string.
                Karaoke ID.
            list_type  : int string.

        """
        song_langs = params.get_list('song_langs', [])
        song_langs = [v for v in song_langs if v in ARG_SONG_LANGS]
        if not song_langs:
            raise RequestError(ERR_REQUEST_ARG, str_data=('song_langs.'))

        list_type = params.get_int('list_type', 1)
        if list_type not in xrange(1, 4):
            raise RequestError(ERR_REQUEST_ARG, str_data=('list_type.'))

        # Take the cover of first row of each song lang as index cover.
        table = u'''
            song_content, 
            (
                SELECT   DISTINCT ON (lang)
                         keymd5, lang
                FROM     song_list
                WHERE    lang IN %(lang)s AND type=%(type)s
                ORDER BY lang, rank
            ) l
        '''
        fields = ['song_content.poster_url', 'l.lang']
        condition = [u'song_content.keymd5=l.keymd5', {'lang': tuple(song_langs), 'type': str(list_type)}]

        db_inst = PGClient(db='karaoke')

        rows, rowcount = db_inst.query(table=table, fields=fields, condition=condition, ret_type='all')
        covers = {row['lang']: row['poster_url'] for row in rows}

        return Response(content={
            'covers': {key: covers[key] if covers.has_key(key) else None for key in song_langs}
        })
Example #24
0
def param2bool(string, default=None):
    """ Boolean string cast to boolean.
    Args:
        string: "True" or "False"
    """
    if not string:
        return default

    ret = _eval_cast(string)

    if not isinstance(ret, bool):
        raise RequestError(ERR_REQUEST_ARG, str_data=('not a bool'))
    return ret
Example #25
0
    def get_metadata(self, url, params, request, response):
        """ Use AV id of any source to get all metadata.
        [Arguments]
            ids : list string.
                IDs of AVs to get metadata.
        """
        # API args
        ids = params.get_list('ids', None)
        if not ids:
            raise RequestError(ERR_REQUEST_ARG, str_data=('ids.'))

        resp = Response(content={'avs': {}})

        db_inst = PGClient(db='av')

        condition = ['id IN %(id)s', {'id': tuple(ids)}]
        avs, rowcount = db_inst.query(table='video',
                                      condition=condition,
                                      ret_type='all')

        for av in avs:
            resp['avs'][av['id']] = {'metadata': {}}

            resp['avs'][av['id']]['metadata'][DB_SRC_DMM] = {
                'id': av['id'],
                'source': DB_SRC_DMM,
                'title': av['title'],
                'posterurl': av['posterurl'],
                'duration': av['duration'],
                'stars': av['performer'],
                'genres': av['category'],
                'rating': av['rating'],
                'maker': av['maker'],
                'series': av['series'],
                'date': av['date']
                and av['date'].strftime("%Y-%m-%d %H:%M:%S"),
                'description': av['description'],
                'samples': av['samples'],
                'url': av['url'],
                'date2': av['date2']
                and av['date2'].strftime("%Y-%m-%d %H:%M:%S"),
                'code': av['code'],
                'md5sum': av['md5sum']
            }

        # patch up not found data.
        for missing_id in set(ids).difference(resp['avs'].keys()):
            resp['avs'][missing_id] = {}

        return resp
Example #26
0
 def copy_from(self,
               fileobj,
               table,
               sep='\t',
               null='\\N',
               size=8192,
               columns=None):
     try:
         self.cur.copy_from(fileobj, table, sep, null, size, columns)
         return self.cur
     except Exception, e:
         self.conn.rollback()
         # TODO: write log if we need.
         raise RequestError(ERR_DB_QUERY)
Example #27
0
    def update_latest_movies(self, url, params, request, response):
        """ Update latest movies.

        [Arguments]
            imdbid_list: list
                The imdbid list to update latest table.
        [Return]
            rowcount: int
                The row count of 'movie_latest'.
        """
        imdbid_list = params.get_list('imdbid_list', None)
        # check argument 'imdbid_list'
        if not imdbid_list:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'imdbid_list' is need.", ))

        values = ''
        for data in imdbid_list:
            # check imdbid pattern
            reslut = re.match('tt\d{7}', data[0])
            if reslut is None:
                raise RequestError(
                    ERR_REQUEST_ARG,
                    str_data=('imdbid_list(%s) is not imdbid format.' %
                              data[0], ))
            values += "('%s', '%s')," % (data[0], data[1])
        values = values[:-1]

        # insert db
        db_inst = PGClient()
        db_inst.execute('truncate table movie_latest')
        db_inst.execute('insert into movie_latest values %s' % values)
        db_inst.commit()
        cur = db_inst.execute('select count(*) from movie_latest')
        rowcount = cur.fetchone()[0]

        return Response(model_data={'rowcount': rowcount})
Example #28
0
    def suggest_keyword(self, url, params, request, response):
        """ Suggest keyword.

        [Arguments]
            media_type : string.
                Media type.
            media_id   : string.
                Target media ID.
            lang       : string.
                To choose display content.
        """
        media_type = params.get_string('media_type', None)
        media_id = params.get_string('media_id', None)
        lang = params.get_string('lang', None)

        if not media_id or not media_type or not lang:
            raise RequestError(ERR_REQUEST_ARG, str_data=('args missing'))

        try:
            search_inst = {'movies': m_keyword, 'avs': a_keyword}[media_type]
        except:
            raise RequestError(ERR_REQUEST_ARG, str_data=('media_type error'))

        return Response(content={'keywords': search_inst(media_id, lang)})
Example #29
0
    def get_last_timestamp(self, url, params, request, response):
        """ get last update timestamp

        create patcher by tablename and get timestamp.

        :param url:
        :param params:
        :param request:
        :param response:
        :return:
        """
        tablename = params.get_string('entity', None)
        # check entity argument
        if not tablename:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'entity' is need.", ))

        # get patcher
        patcher = create_patcher(tablename)
        if not patcher:
            raise RequestError(ERR_REQUEST_ARG,
                               str_data=("argument 'entity' is incorrect.", ))

        return Response(model_data={'timestamp': patcher.get_timestamp()})
Example #30
0
    def default(self, *vpath, **params):
        """Dispatcher
        """
        try:
            method = getattr(self, cherrypy.request.method, None)
            if not method:
                cherrypy.response.headers["Allow"] = ",".join(
                    ALLOW_HTTP_METHOD)
                raise RequestError(http_status=405)

            response = method(vpath, Parameters(params), cherrypy.request,
                              cherrypy.response)
            if isinstance(response, Response) and not response.is_success():
                cherrypy.response.status = response.http_status
            return response
        except:
            raise
Example #31
0
    def get_metadata(self, url, params, request, response):
        """ Use karaokes id of any source to get all metadata.
        [Arguments]
            ids : list string.
                IDs of karaokes to get metadata.
        """
        # API args
        ids = params.get_list('ids', None)
        if not ids:
            raise RequestError(ERR_REQUEST_ARG, str_data=('ids.'))

        resp = Response(content={'karaokes': {}})

        db_inst = PGClient(db='karaoke')

        condition = ['keymd5 IN %(id)s', {'id': tuple(ids)}]
        karaokes, rowcount = db_inst.query(table='songs', condition=condition, ret_type='all')

        for karaoke in karaokes:
            resp['karaokes'][karaoke['keymd5']] = {'metadata': {}}

            # Get content data.
            condition = ['keymd5 = %(id)s', {'id': karaoke['keymd5']}]
            rows, rowcount = db_inst.query(table='song_content', condition=condition, ret_type='all')
            content = rows[0] if rows else {}

            # Key name use lang instead of source name and we only have TCH data,
            # because TCH dad is composed of cashbox, holiday and youtube.
            resp['karaokes'][karaoke['keymd5']]['metadata'][LANG_TCH] = {
                'id': karaoke['keymd5'],
                'source': karaoke['source'],
                'title': karaoke['title'],
                'artist': karaoke['artist'],
                'song_lang': karaoke['lang'],
                # use content
                'posterurl': content['poster_url'],
                'description': content['description'],
                'play_url': content['play_url'],
                'md5sum': content['md5sum']
            }

        # patch up not found data.
        for missing_id in set(ids).difference(resp['karaokes'].keys()):
            resp['karaokes'][missing_id] = {}

        return resp