class Reply(Document): """评论回复 owner: 回复者 reply: 被用户回复的评论回复 comment: 所属评论ID content: 内容 create_at: 创建时间 """ collection = DB.replies CACHED_OBJS = CacheDict(max_len=100, max_age_seconds=30) COMMENT_REPLY_IDS = 'reply:comment:%(cid)s' # 评论的回复队列 def format(self): _reply = Reply.get_one(str(self.reply), check_online=False) from_user = User.get_one(str(self.owner), check_online=False) to_user = User.get_one(str(_reply.owner), check_online=False) if _reply else None data = { 'reply_id': str(self._id), 'reply_from': from_user and from_user.format(exclude_fields=['is_followed']), 'reply_to': to_user and to_user.format(exclude_fields=['is_followed']), 'content': self.content, 'reply_count': self.reply_count or 0, 'create_at': self.create_at } return data def create_model(self): _id = super(Reply, self).create_model() if _id: # 更新评论回复数 comment = Comment.get_one(str(self.comment), check_online=False) comment.update_model({'$inc': {'reply': 1}}) # 更新回复的回复数 if self.reply: _reply = Reply.get_one(str(self.reply), check_online=False) _reply.update_model({'$inc': {'reply_count': 1}}) key = self.COMMENT_REPLY_IDS % ({'cid': str(self.comment)}) # 列表为空时key对应的value是一个string try: if Redis.exists(key): Redis.zadd(key, self.create_at, str(_id)) except exceptions.ResponseError: Redis.delete(key) return _id def delete_model(self): ret = super(Reply, self).delete_model() self.collection.update({"reply": self._id}, {"$set": {"reply": None}}) if ret: # 更新评论回复数 comment = Comment.get_one(str(self.comment), check_online=False) comment.update_model({'$inc': {'reply': -1}}) # 更新回复的回复数 if self.reply: _reply = Reply.get_one(str(self.reply), check_online=False) _reply.update_model({'$inc': {'reply_count': -1}}) key = self.COMMENT_REPLY_IDS % ({'cid': str(self.comment)}) try: Redis.zrem(key, str(self._id)) except exceptions.ResponseError: Redis.delete(key) return ret @classmethod def init(cls): doc = super(Reply, cls).init() doc.reply_count = 0 return cls(doc) @classmethod def delete_comment_replies(cls, cid): ret = cls.collection.delete_many({'comment': ObjectId(cid)}) return ret.deleted_count @classmethod @util.cached_zset(lambda cls, cid: cls.COMMENT_REPLY_IDS % ({ 'cid': cid }), snowslide=True) def _load_comment_reply_ids(cls, cid): replies = list( cls.collection.find({ 'comment': ObjectId(cid) }, { '_id': 1, 'create_at': 1 }).sort("create_at", pymongo.DESCENDING)) ret = list() for c in replies: if 'create_at' in c: ret.extend([c['create_at'], str(c['_id'])]) return tuple(ret) @classmethod def comment_reply_ids(cls, cid, page=None, pagesize=None, maxs=None): """获取评论的回复id """ key = cls.COMMENT_REPLY_IDS % ({'cid': cid}) if not Redis.exists(key): cls._load_comment_reply_ids(cid) try: if maxs: ids = Redis.zrangebyscore(key, '(%.6f' % (maxs), '+inf', start=0, num=pagesize) else: start = (page - 1) * pagesize stop = start + pagesize - 1 ids = Redis.zrange(key, start, stop) except exceptions.ResponseError: # 列表为空时key对应的value是一个string ids = [] return list(ids)
class DaTangVideo(Document): """视频 """ collection = DB.datang_videos CACHED_OBJS = CacheDict(max_len=100, max_age_seconds=5) GAME_HOTVIDEO_IDS = "dtvideos:hotgame:%(date)s:%(gid)s" # 游戏人气视频队列 USER_GAME_VIDEO_IDS = "dtvideos:user:%(uid)s:game:%(gid)s" # 用户为某个游戏创建的视频队列 def format(self, exclude_fields=[]): author_ex_fields = [] game_ex_fields = [] for field in exclude_fields: if field.startswith('author__'): _, _field = field.split('__') author_ex_fields.append(_field) elif field.startswith('game__'): _, _field = field.split('__') game_ex_fields.append(_field) vid = str(self._id) data = { 'video_id': vid, 'ratio': self.ratio, 'title': self.title, 'url': '%s/datang/videos/%s/play' % (app.config.get('SERVER_URL'), vid), 'is_liked': False, 'author': self.author, 'cover': "%s%s" % (app.config.get("MEDIA_URL"), self.cover), 'like_count': self.like, 'create_at': self.create_at, 'is_favored': False, 'game': self.game, 'comment_count': self.comment_count, 'vv': self.vv, 'duration': self.duration and int(self.duration), 'tags': self.tags, 'share_url': None, 'status': self.status if self.status else None, 'is_online': self.online } return data @property def online(self): _is_online = super(DaTangVideo, self).online return _is_online or self.status in [const.ELITE] def create_model(self): _id = super(DaTangVideo, self).create_model() if _id: if self.online: key = self.USER_GAME_VIDEO_IDS % ({'uid': str(self.author), 'gid': str(self.game)}) try: if Redis.exists(key): Redis.zadd(key, self.create_at, str(_id)) except exceptions.ResponseError: Redis.delete(key) return _id def update_model(self, data={}): obj = super(DaTangVideo, self).update_model(data) if obj: # 下线-->上线 if self.offline and obj.online: key = self.USER_GAME_VIDEO_IDS % ({'uid': str(self.author), 'gid': str(self.game)}) try: if Redis.exists(key): Redis.zadd(key, self.create_at, str(self._id)) except exceptions.ResponseError: Redis.delete(key) return obj def delete_model(self): ret = super(DaTangVideo, self).delete_model() if ret: key = self.USER_GAME_VIDEO_IDS % ({'uid': str(self.author), 'gid': str(self.game)}) try: Redis.zrem(key, str(self._id)) except exceptions.ResponseError: Redis.delete(key) return ret def real_url(self): if self.url.lower().startswith('http://'): return self.url return urljoin(app.config.get('VIDEO_URL'), self.url) @classmethod def init(cls): doc = super(DaTangVideo, cls).init() doc.cover = 0 doc.url = '' doc.like = 0 doc.vv = 0 doc.comment_count = 0 doc.status = const.ONLINE return cls(doc) @classmethod @util.cached_list(lambda cls, gid: cls.GAME_HOTVIDEO_IDS % ({'date': time.strftime("%Y%m%d"), 'gid': gid}), timeout=86400, snowslide=True) def _load_game_hotvideo_ids(cls, gid): videos = list(cls.collection.find( { 'game': ObjectId(gid), 'create_at': {'$gte': int(time.time()) - const.POPULAR_VIDEO.get("time_range")}, '$or': [{'status': {'$exists': False}}, {'status': {'$in': [const.ONLINE, const.ELITE]}}] }, {'_id': 1, 'vv': 1} ).sort("vv", pymongo.DESCENDING)) if len(videos) < const.POPULAR_VIDEO.get("max_video_num", 30): videos = list(cls.collection.find( { 'game': gid, 'create_at': {'$gte': int(time.time()) - 30 * 24 * 60 * 60}, '$or': [{'status': {'$exists': False}}, {'status': {'$in': [const.ONLINE, const.ELITE]}}] }, {'_id': 1, 'vv': 1} ).sort("vv", pymongo.DESCENDING)) vids = [v['_id'] for v in videos] return vids @classmethod def game_hotvideo_ids(cls, gid, page, pagesize, maxs=None): key = cls.GAME_HOTVIDEO_IDS % ({'date': time.strftime("%Y%m%d"), 'gid': gid}) if not Redis.exists(key): cls._load_game_hotvideo_ids(gid) try: start = (page - 1) * pagesize if page else 0 stop = (start + pagesize - 1) if pagesize else -1 ids = Redis.lrange(key, start, stop) except exceptions.ResponseError: ids = [] return list(ids) @classmethod @util.cached_zset(lambda cls, uid, gid: cls.USER_GAME_VIDEO_IDS % ({'uid': uid, 'gid': gid})) def _load_user_game_video_ids(cls, uid, gid): videos = list(cls.collection.find( { 'author': uid, 'game': gid, '$or': [{'status': {'$exists': False}}, {'status': {'$in': [const.ONLINE, const.ELITE]}}] }, {'_id': 1, 'create_at': 1} ).sort("create_at", pymongo.DESCENDING)) ret = list() for i in videos: ret.extend([i['create_at'], str(i['_id'])]) return tuple(ret) @classmethod def user_game_video_ids(cls, uid, gid, page=None, pagesize=None, maxs=None): key = cls.USER_GAME_VIDEO_IDS % ({'uid': uid, 'gid': gid}) if not Redis.exists(key): cls._load_user_game_video_ids(uid, gid) try: if maxs: ids = Redis.zrevrangebyscore(key, '(%.6f' % (maxs), '-inf', start=0, num=pagesize) else: start = (page - 1) * pagesize stop = start + pagesize - 1 ids = Redis.zrevrange(key, start, stop) except exceptions.ResponseError: ids = [] return list(ids)
class Comment(Document): """评论 """ collection = DB.comments CACHED_OBJS = CacheDict(max_len=100, max_age_seconds=5) VIDEO_COMMENT_IDS = 'comments:video:%(vid)s' # 某个视频的评论队列 def format(self): author = User.get_one(str(self.author), check_online=False) uid = request.authed_user and str(request.authed_user._id) data = { 'author': author and author.format(exclude_fields=['is_followed']), 'comment_id': str(self._id), 'video_id': self.video and str(self.video), 'content': self.content, 'create_at': self.create_at, 'reply': self.reply or 0, 'like': self.like or 0, 'liked': UserLikeComment.get_by_ship(uid, str(self._id)) is not None if uid else False, } return data def create_model(self): _id = super(Comment, self).create_model() if _id: # 更新视频评论数 from wanx.models.video import Video video = Video.get_one(str(self.video), check_online=False) video.update_model({'$inc': {'comment_count': 1}}) key = self.VIDEO_COMMENT_IDS % ({'vid': str(self.video)}) # 列表为空时key对应的value是一个string try: if Redis.exists(key): Redis.zadd(key, self.create_at, str(_id)) except exceptions.ResponseError: Redis.delete(key) return _id def delete_model(self): ret = super(Comment, self).delete_model() if ret: # 更新视频评论数 from wanx.models.video import Video video = Video.get_one(str(self.video), check_online=False) video.update_model({'$inc': {'comment_count': -1}}) key = self.VIDEO_COMMENT_IDS % ({'vid': str(self.video)}) try: Redis.zrem(key, str(self._id)) except exceptions.ResponseError: Redis.delete(key) # 删除全部回复 Reply.delete_comment_replies(str(self._id)) return ret @classmethod def init(cls): doc = super(Comment, cls).init() doc.like = 0 doc.reply = 0 doc.status = const.ONLINE return cls(doc) @classmethod @util.cached_zset(lambda cls, video_id: cls.VIDEO_COMMENT_IDS % ({ 'vid': video_id }), snowslide=True) def _load_video_comment_ids(cls, video_id): comments = list( cls.collection.find( { 'video': ObjectId(video_id), '$or': [{ 'status': { '$exists': False } }, { 'status': 0 }] }, { '_id': 1, 'create_at': 1 }).sort("create_at", pymongo.DESCENDING)) ret = list() for c in comments: if 'create_at' in c: ret.extend([c['create_at'], str(c['_id'])]) return tuple(ret) @classmethod def video_comment_ids(cls, video_id, page=None, pagesize=None, maxs=None): """获取视频相关评论的id """ key = cls.VIDEO_COMMENT_IDS % ({'vid': video_id}) if not Redis.exists(key): cls._load_video_comment_ids(video_id) try: if maxs: ids = Redis.zrevrangebyscore(key, '(%.6f' % (maxs), '-inf', start=0, num=pagesize) else: start = (page - 1) * pagesize stop = start + pagesize - 1 ids = Redis.zrevrange(key, start, stop) except exceptions.ResponseError: # 列表为空时key对应的value是一个string ids = [] return list(ids) @classmethod def video_comment_count(cls, video_id): key = cls.VIDEO_COMMENT_IDS % ({'vid': video_id}) if not Redis.exists(key): cls._load_video_comment_ids(video_id) try: count = Redis.zcard(key) except exceptions.ResponseError: count = 0 return count