Sistema de Comentários e Estatísticas
Para permitir que os usuários interajam com o conteúdo e para rastrear métricas de engajamento, implementamos um formulário de revisão e atualizamos a lógica da rota de reprodução. O formulário é construído utilizando o WTForms para validação integrada.
class ReviewForm(FlaskForm):
review_text = TextAreaField(
'Seu Comentário',
validators=[DataRequired(message="O comentário não pode estar vazio.")],
render_kw={"id": "review-input", "placeholder": "Deixe sua opinião..."}
)
submit_btn = SubmitField(
'Publicar',
render_kw={"class": "btn btn-primary", "id": "submit-review"}
)
A rota de reprodução precisa gerenciar a exibição do vídeo, a paginação dos comentários e o incremento das estatísticas de visualização e quantidade de comentários. Para evitar que a contagem de visualizações aumente duplicadamente ao enviar um comentário, o incremento de visualização é condicionado ao método da requisição.
@main_blueprint.route("/assistir/<movie_id>/<page_num>/", methods=["GET", "POST"])
def stream_movie(movie_id, page_num=1):
film = Movie.query.join(Tag).filter(
Tag.id == Movie.tag_id,
Movie.id == movie_id
).first_or_404()
reviews = Review.query.join(User).filter(
Review.movie_id == film.id
).order_by(Review.created_at.desc()).paginate(
page=page_num, per_page=15, error_out=False
)
form = ReviewForm()
if current_user.is_authenticated and form.validate_on_submit():
new_review = Review(
text=form.review_text.data,
movie_id=film.id,
user_id=current_user.id
)
db.session.add(new_review)
film.review_count = (film.review_count or 0) + 1
db.session.commit()
flash('Comentário publicado com sucesso!', 'success')
return redirect(url_for('main.stream_movie', movie_id=film.id, page_num=1))
if request.method == "GET":
film.view_count = (film.view_count or 0) + 1
db.session.commit()
return render_template("stream.html", film=film, form=form, reviews=reviews)
</page_num></movie_id>
Gerenciamento de Favoritos via AJAX
A funcionalidade de adicionar filmes aos favoritos é processada de forma assíncrona para evitar recarregamentos de página. A rota abaixo verifica se o item já existe na base de dados antes de inserir um novo registro.
@main_blueprint.route("/favoritos/adicionar/", methods=["POST"])
@login_required
def add_to_favorites():
payload = request.get_json()
user_id = current_user.id
film_id = payload.get("movie_id")
existing_fav = Favorite.query.filter_by(
user_id=user_id,
movie_id=film_id
).first()
if existing_fav:
return jsonify({"status": "already_favorited", "message": "Filme já está nos favoritos."})
new_fav = Favorite(user_id=user_id, movie_id=film_id)
db.session.add(new_fav)
db.session.commit()
return jsonify({"status": "success", "message": "Adicionado aos favoritos!"})
No frontend, utilizaremos a API Fetch nativa do JavaScript para realizar a requisição POST, substituindo abordagens antigas com jQuery. Isso torna o código mais limpo e moderno.
document.addEventListener("DOMContentLoaded", function() {
const favBtn = document.getElementById("btn-favorite");
if(favBtn) {
favBtn.addEventListener("click", function() {
const movieId = this.dataset.movieId;
fetch("/favoritos/adicionar/", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": getCsrfToken() // Função auxiliar para obter o token CSRF
},
body: JSON.stringify({ movie_id: movieId })
})
.then(response => response.json())
.then(data => {
const msgContainer = document.getElementById("fav-message");
msgContainer.textContent = data.message;
});
});
}
});
Integração de Danmaku com Redis e DPlayer
O recurso de danmaku (comentários sobrepostos no vídeo) exige alta performance de leitura e escrita. Utilizamos o Redis como fila de mensagens para armazenar e recuperar os comentários em tempo real, integrando-o ao player DPlayer.
@main_blueprint.route("/api/danmaku/<int:film_id>/", methods=["GET", "POST"])
def manage_danmaku(film_id):
redis_key = f"danmaku:film:{film_id}"
if request.method == "GET":
raw_messages = redis_client.lrange(redis_key, 0, -1)
formatted_data = [json.loads(msg) for msg in raw_messages]
return jsonify({"code": 0, "data": formatted_data})
elif request.method == "POST":
payload = request.get_json()
danmaku_obj = {
"author": payload.get("author", "Anônimo"),
"time": payload.get("time", 0),
"text": payload.get("text", ""),
"color": payload.get("color", 16777215),
"type": payload.get("type", 0)
}
redis_client.lpush(redis_key, json.dumps(danmaku_obj))
response_obj = {
"code": 0,
"data": {
"_id": uuid.uuid4().hex,
"player": payload.get("id"),
"author": danmaku_obj["author"],
"time": danmaku_obj["time"],
"text": danmaku_obj["text"],
"color": danmaku_obj["color"],
"type": danmaku_obj["type"],
"ip": request.remote_addr,
"date": int(time.time() * 1000)
}
}
return jsonify(response_obj)
Para o front end, o DPlayer é configurado apontando para a API de danmaku criada. Certifique-se de incluir os arquivos CSS e JS necessários do DPlayer, bem como as bibliotecas de suporte para streaming (como flv.js ou hls.js).
<link rel="stylesheet" href="{{ url_for('static', filename='dplayer/DPlayer.min.css') }}">
<script src="https://cdn.jsdelivr.net/npm/flv.js/dist/flv.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hls.js/dist/hls.min.js"></script>
<script src="{{ url_for('static', filename='dplayer/DPlayer.min.js') }}"></script>
<div id="video-player"></div>
<script>
const dp = new DPlayer({
container: document.getElementById('video-player'),
video: {
url: "{{ url_for('static', filename='media/' + film.video_file) }}",
},
danmaku: {
id: '{{ film.id }}',
api: "/api/danmaku/",
}
});
</script>
A configuração do cliente Redis no aplicativo Flask é feita através da extensão flask-redis, que deve ser instalada via pip (pip install flask-redis).
from flask_redis import FlaskRedis
app.config['REDIS_URL'] = 'redis://localhost:6379/2'
redis_client = FlaskRedis(app)
Considerações para Ambiente de Produção
Ao migrar a aplicação para um ambiente de produção, é fundamental utilizar servidores WSGI robustos como Gunicorn ou uWSGI, acompanhados de um proxy reverso como Nginx para gerenciar requisições estáticas e balanceamento de carga. Além disso, o serviço do Redis deve ser configurado com persistência e políticas de segurança adequadas, e as variáveis de ambiente devem ser gerenciadas fora do código-fonte.