17 -Flask构建弹幕微电影网站- 电影播放及评论弹幕收藏实现

17 -Flask构建弹幕微电影网站- 电影播放及评论弹幕收藏实现

大家好,又见面了,我是全栈君。

上映预告

  1. 模型: Preview
  2. 表单: 无
  3. 请求方法: GET
  4. 访问控制: 无

views中进行业务逻辑的实现

@home.route("/animation/")
def animation():
    """
    首页轮播动画
    """
    data = Preview.query.all()
    return render_template("home/animation.html", data=data)

标签筛选 – 电影分页

  1. 模型: Tag Movie
  2. 表单: 无
  3. 请求方法: GET
  4. 访问控制: 无

编写首页的views

@home.route("/<int:page>/", methods=["GET"])
@home.route("/", methods=["GET"])
def index(page=None):
    """
    首页电影列表
    """
    tags = Tag.query.all()
    page_data = Movie.query
    # 标签
    tid = request.args.get("tid", 0)
    if int(tid) != 0:
        page_data = page_data.filter_by(tag_id=int(tid))
    # 星级
    star = request.args.get("star", 0)
    if int(star) != 0:
        page_data = page_data.filter_by(star=int(star))
    # 时间
    time = request.args.get("time", 0)
    if int(time) != 0:
        if int(time) == 1:
            page_data = page_data.order_by(
                Movie.addtime.desc()
            )
        else:
            page_data = page_data.order_by(
                Movie.addtime.asc()
            )
    # 播放量
    pm = request.args.get("pm", 0)
    if int(pm) != 0:
        if int(pm) == 1:
            page_data = page_data.order_by(
                Movie.playnum.desc()
            )
        else:
            page_data = page_data.order_by(
                Movie.playnum.asc()
            )
    # 评论量
    cm = request.args.get("cm", 0)
    if int(cm) != 0:
        if int(cm) == 1:
            page_data = page_data.order_by(
                Movie.commentnum.desc()
            )
        else:
            page_data = page_data.order_by(
                Movie.commentnum.asc()
            )
    if page is None:
        page = 1
    page_data = page_data.paginate(page=page, per_page=8)
    p = dict(
        tid=tid,
        star=star,
        time=time,
        pm=pm,
        cm=cm,
    )
    return render_template(
        "home/index.html",
        tags=tags,
        p=p,
        page_data=page_data)

电影标签,电影星级,上映时间,播放数量,评论数量

  • tid = request.args.get("tid", 0) 获取到问号传参中的tid没有获取到时置零
  • 同理获取starstar = request.args.get("star", 0)
  • time = request.args.get("time", 0)同理获取到time
  • 其他同理pm cm 这里的数量排序通过前台传过来是0还是1决定数量正序或倒序

碎碎念杂谈,将用户已选择的分类细节展示给用户,可以点击x去除。

mark

mark

可以使用jquery实现,清除组合的时候移除dom节点,显示的时候添加dom节点,然后在添加和清除的时候注意参数的添加和删除,组装成url,通过触发事件触发搜素!

查询出所有的tag,然后经模板渲染出菜单。

tags = Tag.query.all()

传递到模板进行填充。

mark

mark

p = dict(
        tid=tid,
        star=star,
        time=time,
        pm=pm,
        cm=cm,
    )

构建一个参数的字典。

mark

mark

处理标签的链接

 href="{
    
    { url_for('home.index') }}?tid={
    
    { v.id }}&star={
    
    { p['star'] }}&time={
    
    { p['time'] }}&pm={
    
    { p['pm'] }}&cm={
    
    { p['cm'] }}" 

上面为标签添加href值。其中的tid是取自当前循环

 href="{
    
    { url_for('home.index') }}?tid={
    
    { p['tid'] }}&star={
    
    { v.star }}&time={
    
    { p['time'] }}&pm={
    
    { p['pm'] }}&cm={
    
    { p['cm'] }}" 

上面为star添加href值。其中的star是取自当前循环

其他项同理可得,将当前值取实际值,其他值取p[”]

分页电影

    if int(tid) != 0:
        page_data = page_data.filter_by(tag_id=int(tid))

其他字段做相同的过滤操作。

进行分页效果展示。

mark

mark

为所有的标签分类添加page=1

mark

mark

电影搜索,搜索分页

  1. 模型: Movie
  2. 表单: 无
  3. 请求方法: GET
  4. 访问控制: 无

views中书写search相关的逻辑处理

@home.route("/search/<int:page>/")
def search(page=None):
    """
    搜索
    """
    if page is None:
        page = 1
    key = request.args.get("key", "")
    movie_count = Movie.query.filter(
        Movie.title.ilike('%' + key + '%')
    ).count()
    page_data = Movie.query.filter(
        Movie.title.ilike('%' + key + '%')
    ).order_by(
        Movie.addtime.desc()
    ).paginate(page=page, per_page=10)
    page_data.key = key
    return render_template("home/search.html", movie_count=movie_count, key=key, page_data=page_data)
mark

mark

为layout中的搜索框添加id

添加id = do_search

mark

mark

mark

mark

home页面中script标签中使用jQuery获取搜索框内key值。然后location跳转到search路由,并携带page key 参数

将这段代码再粘贴到其他需要搜索的页面

  • layout home 都需要修改id

ilike进行模糊匹配

在search页面进行最终结果的填充与分页。

又是这种重复的填充操作,自己瞎做吧。跟以前没什么两样。

新建一个s_page.html,把home_page复制过去就行。

然后依然万年不变import 调用page方法。

碎碎念积累,回车直接搜索:

$("...").keydown(function(e){
if(e.keyCode==13){
    //处理事件
}
});

电影详情 电影播放 电影评论 电影收藏

  1. 模型: Movie
  2. 表单: 无
  3. 请求方法: GET
  4. 访问控制: 无

评论统计:

  1. 模型: Movie User Comment
  2. 表单: CommentForm
  3. 请求方法: GET POST
  4. 访问控制: 需登录
mark

mark

如果当前未登录则显示请先登录才可以提交。

mark

mark

只有用户登录状态才会显示评论框。

forms中添加commentsform

class CommentForm(FlaskForm):
    content = TextAreaField(
        label="内容",
        validators=[
            DataRequired("请输入内容!"),
        ],
        description="内容",
        render_kw={
            "id": "input_content"
        }
    )
    submit = SubmitField(
        '提交评论',
        render_kw={
            "class": "btn btn-success",
            "id": "btn-sub"
        }
    )

注意内容的id input_content

views中处理播放页面的数据业务逻辑,以及评论的业务逻辑

@home.route("/play/<int:id>/<int:page>/", methods=["GET", "POST"])
def play(id=None, page=None):
    """
    播放电影
    """
    # 查询出相关联的标签
    movie = Movie.query.join(Tag).filter(
        Tag.id == Movie.tag_id,
        Movie.id == int(id)
    ).first_or_404()

    if page is None:
        page = 1
    page_data = Comment.query.join(
        Movie
    ).join(
        User
    ).filter(
        Movie.id == movie.id,
        User.id == Comment.user_id
    ).order_by(
        Comment.addtime.desc()
    ).paginate(page=page, per_page=10)

    movie.playnum = movie.playnum + 1
    form = CommentForm()
    # 必须登录
    if "user" in session and form.validate_on_submit():
        data = form.data
        comment = Comment(
            content=data["content"],
            movie_id=movie.id,
            user_id=session["user_id"]
        )
        db.session.add(comment)
        db.session.commit()
        movie.commentnum = movie.commentnum + 1
        db.session.add(movie)
        db.session.commit()
        flash("添加评论成功!", "ok")
        return redirect(url_for('home.play', id=movie.id, page=1))
    db.session.add(movie)
    db.session.commit()
    return render_template("home/play.html", movie=movie, form=form, page_data=page_data)

index以及search页面中的播放按钮,都添加上v.id 以及page

前往play中填充movie的具体数据。使用form填充评论框,使用pagedata填充已有评论

  • 当然flash的位置,报错信息。submit之前的csrf都不要忘记,还有form的method
mark

mark

修改js中需要修改的player file title 等

为评论分页新建一个html ui/comment_page.html复制s_page的内容

为commentspage添加id参数。url中也添加id

然后又是重复的引入和page方法调用。pagedata填充

为了安全,默认不会显示html的内容,添加safe

mark

mark

查询出自己的评论记录显示在自己个人中心

@home.route("/comments/<int:page>/")
@user_login_req
def comments(page=None):
    """
    个人中心评论记录
    """
    if page is None:
        page = 1
    page_data = Comment.query.join(
        Movie
    ).join(
        User
    ).filter(
        Movie.id == Comment.movie_id,
        User.id == session["user_id"]
    ).order_by(
        Comment.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template("home/comments.html", page_data=page_data)

将comments.html进行pagedata填充以及分页处理。

menu中评论记录url添加page=1

mark

mark

收藏电影

  1. 模型: Movie User Moviecol
  2. 表单: 无
  3. 请求方法: GET
  4. 访问控制: 需登录

通过ajax异步方法添加电影收藏。

mark

mark

ajax的提示信息无处安放,添加上图id 为show_col_msg

play页面中有一个按钮是收藏电影,id为btn-col

mark

mark

为btn-col 设置点击事件,获取当前的movieid,获取当前的用户。
使用ajax,提交的地址。type:get,提交的数据,返回的数据类型json。
成功之后的处理函数res接收返回的json对象

@home.route("/moviecol/add/", methods=["GET"])
@user_login_req
def moviecol_add():
    """
    添加电影收藏
    """
    uid = request.args.get("uid", "")
    mid = request.args.get("mid", "")
    moviecol = Moviecol.query.filter_by(
        user_id=int(uid),
        movie_id=int(mid)
    ).count()
    # 已收藏
    if moviecol == 1:
        data = dict(ok=0)
    # 未收藏进行收藏
    if moviecol == 0:
        moviecol = Moviecol(
            user_id=int(uid),
            movie_id=int(mid)
        )
        db.session.add(moviecol)
        db.session.commit()
        data = dict(ok=1)
    import json
    return json.dumps(data)

处理个人中心中电影收藏的view

@home.route("/moviecol/<int:page>/")
@user_login_req
def moviecol(page=None):
    """
    电影收藏
    """
    if page is None:
        page = 1
    page_data = Moviecol.query.join(
        Movie
    ).join(
        User
    ).filter(
        Movie.id == Moviecol.movie_id,
        User.id == session["user_id"]
    ).order_by(
        Moviecol.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template("home/moviecol.html", page_data=page_data)

page_data进行填充,进行分页

menus中将收藏电影

mark

mark

mark

mark

修改这里配置菜单显示哪些按钮。

  • 换不存在的账号登录出现报错
builtins.AttributeError
AttributeError: 'NoneType' object has no attribute 'check_pwd'

解决方案:

        user = User.query.filter_by(name=data["name"]).first()
        if user:
            if not user.check_pwd(data["pwd"]):
                flash("密码错误!", "err")
                return redirect(url_for("home.login"))
        else:
            flash("账户不存在!", "err")
            return redirect(url_for("home.login"))

FLask 结合Redis消息队列实现电影弹幕

  1. 模型: Movie
  2. 表单: 无
  3. 请求方法: GET POST
  4. 访问控制: 无
  5. 消息队列: Redis
  6. Flask第三方扩展: Flask-Redis
  7. 弹幕播放器插件: dplayer.js(开源)

redis自行前往官网下载最新稳定版本

pip install flask-redis

前面的版本使用的是jwplayer

yum -y install gcc gcc-c++
tar zxf redis-4.0.1.tar.gz
cd redis-4.0.1/

可以看到当前目录有Makefile文件,安装。

make && make install

启动服务

./utils/install_server.sh
vim /etc/redis/6379.conf

修改bind的地址

redis-cli -h 192.x.x.x

弹幕播放器views实现

@home.route("/video/<int:id>/<int:page>/", methods=["GET", "POST"])
def video(id=None, page=None):
    """
    弹幕播放器
    """
    movie = Movie.query.join(Tag).filter(
        Tag.id == Movie.tag_id,
        Movie.id == int(id)
    ).first_or_404()

    if page is None:
        page = 1
    page_data = Comment.query.join(
        Movie
    ).join(
        User
    ).filter(
        Movie.id == movie.id,
        User.id == Comment.user_id
    ).order_by(
        Comment.addtime.desc()
    ).paginate(page=page, per_page=10)

    movie.playnum = movie.playnum + 1
    form = CommentForm()
    if "user" in session and form.validate_on_submit():
        data = form.data
        comment = Comment(
            content=data["content"],
            movie_id=movie.id,
            user_id=session["user_id"]
        )
        db.session.add(comment)
        db.session.commit()
        movie.commentnum = movie.commentnum + 1
        db.session.add(movie)
        db.session.commit()
        flash("添加评论成功!", "ok")
        return redirect(url_for('home.video', id=movie.id, page=1))
    db.session.add(movie)
    db.session.commit()
    return render_template("home/video.html", movie=movie, form=form, page_data=page_data)

templates目录创建一个video.html把原本play中的代码粘贴过来

将原本的jwplayer部分的script可以直接全部去掉了

mark

mark

将评论这里的分页中视图改为video

将jwplayer的css去掉

进入播放需要的静态资源

<link rel="stylesheet" href="{
    
    { url_for('static',filename='dplayer/dist/DPlayer.min.css') }}">
<script src="{
    
    { url_for('static',filename='dplayer/plugin/flv.min.js') }}"></script>
<script src="{
    
    { url_for('static',filename='dplayer/plugin/hls.min.js') }}"></script>
<script src="{
    
    { url_for('static',filename='dplayer/dist/DPlayer.min.js') }}"></script>

修改样式

    .dplayer-comment-setting-type>label{
    display: inline;

定义播放器的样式

        <div id="dplayer1" style="height:500px;width: 774px;"></div>

播放页面

<script>
    var dp1 = new DPlayer({
        element: document.getElementById('dplayer1'),
        video: {
            url: "{
    
    { url_for('static',filename='uploads/'+movie.url) }}",
        },
        danmaku: {
            id: '{
    
    { movie.id }}',
            api: "/tm/"
        }
    });
</script>

在官方网站的demo上开发者工具xhr可以看到弹幕发送及加载数据的接口。

发送的格式是json的字符串。

定义弹幕相关的处理方法

init文件中

from flask_redis import FlaskRedis

定义我们redis的url格式

app.config["REDIS_URL"] = "redis://127.0.0.1:6379/0"
rd = FlaskRedis(app)

views.py中引入

@home.route("/tm/", methods=["GET", "POST"])
def tm():
    """
    弹幕消息处理
    """
    import json
    if request.method == "GET":
        # 获取弹幕消息队列
        id = request.args.get('id')
        # 存放在redis队列中的键值
        key = "movie" + str(id)
        if rd.llen(key):
            msgs = rd.lrange(key, 0, 2999)
            res = {
                "code": 1,
                "danmaku": [json.loads(v) for v in msgs]
            }
        else:
            res = {
                "code": 1,
                "danmaku": []
            }
        resp = json.dumps(res)
    if request.method == "POST":
        # 添加弹幕
        data = json.loads(request.get_data())
        msg = {
            "__v": 0,
            "author": data["author"],
            "time": data["time"],
            "text": data["text"],
            "color": data["color"],
            "type": data['type'],
            "ip": request.remote_addr,
            "_id": datetime.datetime.now().strftime("%Y%m%d%H%M%S") + uuid.uuid4().hex,
            "player": [
                data["player"]
            ]
        }
        res = {
            "code": 1,
            "data": msg
        }
        resp = json.dumps(res)
        # 将添加的弹幕推入redis的队列中
        rd.lpush("movie" + str(data["player"]), json.dumps(msg))
    return Response(resp, mimetype='application/json')

原理:

mark

mark

代码优化及bug处理

  1. 头像判断
  2. 关键字搜索分页
  3. 电影右侧播放页面滚动条
builtins.TypeError
TypeError: must be str, not NoneType
mark

mark

如果有头像,显示头像,没有头像就显示holder的空头像。

评论记录中的头像部分做相同处理

后台的会员列表也做同样的处理

mark

mark

后台的评论列表也做同样的处理

mark

mark

mark

mark

会员详情中的会员头像也要判断templates/admin/user_view.html

mark

mark

关键字搜索分页

搜索分页中当前key会丢失掉

mark

mark

spage中的key要传递进去。

当影片内容过长设置滚动条

mark

mark

play 和 video中都做同样处理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/107680.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • id门禁卡复制到手机_使用iPhone解锁开门?手机复制门禁卡教程分享

    id门禁卡复制到手机_使用iPhone解锁开门?手机复制门禁卡教程分享使用iPhone解锁开门?手机复制门禁卡教程分享2020-01-1013:31:24173点赞1746收藏262评论对于安卓用户来说,NFC功能是考量一部手机是否是旗舰水准的一项重要指标。而对于iPhone用户,从iPhone6S开始就搭载了NFC功能,却仅仅可以使用ApplePay支付功能,对于国内大部分实体店来说ApplePay的普及程度也不高,NFC功能实际上就一直被闲置着。最近楼主…

  • kali linux 使用教程_kali linux安装软件

    kali linux 使用教程_kali linux安装软件kali-linux激活成功教程wif密码教程kali-linux激活成功教程wif密码教程一.需要准备:(1)安装VMwareWorkstation虚拟机。(2)安装kali-linux系统。(3)准备网卡二.开始激活成功教程(1)插上无线网卡(2)输入ifconfig查看网卡信息,出现wlan0说明连接成功(3)输入airmon-ngstartwlan0开启网卡监听模式(4)输入iwconfig命令查看网卡信息(5)输入airodump-ngwlan0mon(6)数据抓取===操作步骤===(7)解压kali自带的字

  • 如何用手机号申请163邮箱_163邮箱注册手机号注册

    如何用手机号申请163邮箱_163邮箱注册手机号注册如果你还没有邮箱,直接用手机号注册163邮箱,163.net是一款TOM的VIP邮箱,跟普通邮箱的区别是邮箱容量可以无限放大,来往的邮件信息能长期存储,国际邮件能快速收到和发出。怎么申请邮箱?163邮箱申请的好处用手机浏览器输入图片中的网址,进入邮箱官网在这里跟普通邮箱的区别是VIP邮箱有多个后缀选择,不像qq只能有一个。点击注册,接下来选择套餐,根据邮箱名字的位数、容量空间、大附件、群发数量,还有安全防护级别、误发邮件撤回次数、删除的邮件回复次数来选择套餐,不过不用担心,如果你现在已经有邮箱了

  • ubuntu下安装nginx_Linux中安装Nginx

    ubuntu下安装nginx_Linux中安装Nginx1.首先使用dpkg命令查看自己需要的软件是否安装。①、例如查看zlib是否安装:dpkg-l|grepzlib②、依赖包openssl安装,命令:sudoapt-getinstallopenssllibssl-dev③、解决依赖包pcre安装,命令:sudoapt-getinstalllibpcre…

  • Java中关于守护线程_守护线程和主线程

    Java中关于守护线程_守护线程和主线程在Java中有两类线程:UserThread(用户线程)、DaemonThread(守护线程) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是

    2022年10月16日
  • quartz调度问题 没报错也没停止运行 Scheduler class: ‘org.quartz.core.QuartzScheduler‘ – running locally

    quartz调度问题 没报错也没停止运行 Scheduler class: ‘org.quartz.core.QuartzScheduler‘ – running locallySchedulerclass:‘org.quartz.core.QuartzScheduler’-runninglocally.NOTSTARTED.Currentlyinstandbymode.Numberofjobsexecuted:0Usingthreadpool‘org.quartz.simpl.SimpleThreadPool’-with20…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号