Files
Ske_Signage/app/routes/api.py
2025-07-16 08:03:57 +03:00

154 lines
5.2 KiB
Python

"""
API routes for player clients
"""
from flask import Blueprint, request, jsonify, url_for
from app.models.player import Player
from app.models.content import Content
from app.extensions import bcrypt, db
bp = Blueprint('api', __name__)
@bp.route('/playlists', methods=['GET'])
def get_playlists():
"""Get playlist for a player"""
hostname = request.args.get('hostname')
quickconnect_code = request.args.get('quickconnect_code')
# Validate parameters
if not hostname or not quickconnect_code:
return jsonify({'error': 'Hostname and quick connect code are required'}), 400
# Find player and verify credentials
player = Player.query.filter_by(hostname=hostname).first()
if not player or not player.verify_quickconnect_code(quickconnect_code):
return jsonify({'error': 'Invalid hostname or quick connect code'}), 404
# Update last seen
player.last_seen = db.func.current_timestamp()
db.session.commit()
# Get content based on player's group status
if player.is_locked_to_group:
# Player is locked to a group - get shared content
group = player.locked_to_group
player_ids = [p.id for p in group.players]
# Get unique content by filename (first occurrence)
content_query = (
db.session.query(
Content.file_name,
db.func.min(Content.id).label('id'),
db.func.min(Content.duration).label('duration'),
db.func.min(Content.position).label('position'),
db.func.min(Content.content_type).label('content_type')
)
.filter(Content.player_id.in_(player_ids))
.group_by(Content.file_name)
)
content = db.session.query(Content).filter(
Content.id.in_([c.id for c in content_query])
).order_by(Content.position).all()
else:
# Individual player content
content = Content.query.filter_by(player_id=player.id).order_by(Content.position).all()
# Build playlist
playlist = []
for media in content:
playlist.append({
'file_name': media.file_name,
'url': url_for('content.media', filename=media.file_name, _external=True),
'duration': media.duration,
'content_type': media.content_type,
'position': media.position
})
return jsonify({
'playlist': playlist,
'playlist_version': player.playlist_version,
'hashed_quickconnect': player.quickconnect_password,
'player_id': player.id,
'player_name': player.username
})
@bp.route('/playlist_version', methods=['GET'])
def get_playlist_version():
"""Get playlist version for a player (for checking updates)"""
hostname = request.args.get('hostname')
quickconnect_code = request.args.get('quickconnect_code')
# Validate parameters
if not hostname or not quickconnect_code:
return jsonify({'error': 'Hostname and quick connect code are required'}), 400
# Find player and verify credentials
player = Player.query.filter_by(hostname=hostname).first()
if not player or not player.verify_quickconnect_code(quickconnect_code):
return jsonify({'error': 'Invalid hostname or quick connect code'}), 404
# Update last seen
player.last_seen = db.func.current_timestamp()
db.session.commit()
return jsonify({
'playlist_version': player.playlist_version,
'hashed_quickconnect': player.quickconnect_password
})
@bp.route('/player_status', methods=['POST'])
def update_player_status():
"""Update player status (heartbeat)"""
data = request.get_json()
if not data:
return jsonify({'error': 'JSON data required'}), 400
hostname = data.get('hostname')
quickconnect_code = data.get('quickconnect_code')
if not hostname or not quickconnect_code:
return jsonify({'error': 'Hostname and quick connect code are required'}), 400
# Find player and verify credentials
player = Player.query.filter_by(hostname=hostname).first()
if not player or not player.verify_quickconnect_code(quickconnect_code):
return jsonify({'error': 'Invalid hostname or quick connect code'}), 404
# Update player status
player.last_seen = db.func.current_timestamp()
player.is_active = True
# Optional: Update additional status info if provided
if 'status' in data:
# Could store additional status information in the future
pass
db.session.commit()
return jsonify({
'success': True,
'playlist_version': player.playlist_version,
'message': 'Status updated successfully'
})
@bp.route('/health', methods=['GET'])
def health_check():
"""Health check endpoint"""
return jsonify({
'status': 'healthy',
'service': 'SKE Digital Signage Server',
'version': '2.0.0'
})
@bp.errorhandler(404)
def api_not_found(error):
"""API 404 handler"""
return jsonify({'error': 'API endpoint not found'}), 404
@bp.errorhandler(500)
def api_internal_error(error):
"""API 500 handler"""
return jsonify({'error': 'Internal server error'}), 500