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})
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)
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})
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'))
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() })
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
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)
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'))
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]})
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()})
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})
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'] })
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)
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 } })
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
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)
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'))
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
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
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
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() })
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
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} })
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
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
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)
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})
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)})
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()})
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
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