fix: player connectivity and media download pipeline

- nginx: add /api/ shortcut block (no portal auth, X-Script-Name /digiserver,
  Host $http_host) so players can reach DigiServer API without /digiserver prefix
- nginx: use $http_host in /api/ block so Flask host_url includes port — fixes
  media download URLs missing :8080 (was http://ip/digiserver/... not http://ip:8080/...)
- player main.py: fix double-port bug when server_ip already contains a port
  (e.g. 192.168.0.230:8080 was producing http://192.168.0.230:8080:80)
- get_playlists_v2.py: force re-sync when server version differs OR local media
  files are missing on disk — fixes stale playlist after server reset
- digiserver api.py: playlist endpoint builds full media URLs using script_root
  from X-Script-Name header set by nginx
- weblink support, player build/deploy improvements, manage-playlist AJAX prefix fix
This commit is contained in:
ske087
2026-06-29 20:04:11 +03:00
parent f674330b93
commit 4f4e017ad2
16 changed files with 1222 additions and 38 deletions
+144
View File
@@ -936,3 +936,147 @@ def https_config_status():
except Exception as e:
log_action('error', f'Error getting HTTPS status: {str(e)}')
return jsonify({'error': str(e)}), 500
def _player_build_meta_path() -> str:
"""Path to the persisted player-build settings (in the instance folder)."""
from app.utils.player_build import BUILD_META_FILENAME
return os.path.join(current_app.instance_path, BUILD_META_FILENAME)
@admin_bp.route('/build-player', methods=['GET'])
@login_required
@admin_required
def build_player():
"""Display the 'Build player files for deployment' admin page."""
from app.utils.ssh_deploy import get_local_player_code_status
from app.utils.player_build import load_build_settings
player_code_dir = current_app.config['PLAYER_CODE_DIR']
settings = load_build_settings(_player_build_meta_path()) or {}
# Prefill server address from saved settings, else from HTTPS config.
if not settings.get('server_ip'):
https_cfg = HTTPSConfig.get_config()
if https_cfg and (https_cfg.domain or https_cfg.ip_address):
settings.setdefault('server_ip', https_cfg.domain or https_cfg.ip_address)
settings.setdefault('port', str(https_cfg.port or 443))
settings.setdefault('use_https', bool(https_cfg.https_enabled))
# Sensible defaults.
settings.setdefault('repo_url', current_app.config.get('PLAYER_REPO_URL', ''))
settings.setdefault('branch', 'main')
# Prefer the server's real LAN IP over 'localhost' from the proxy host header.
default_host = request.host.split(':')[0]
if default_host in ('localhost', '127.0.0.1', '') or default_host.startswith('127.'):
from app.utils.ssh_deploy import detect_server_ip
default_host = detect_server_ip() or default_host
settings.setdefault('server_ip', default_host)
settings.setdefault('port', '443')
settings.setdefault('use_https', True)
settings.setdefault('verify_ssl', False)
settings.setdefault('orientation', 'Landscape')
settings.setdefault('max_resolution', '1920x1080')
code_status = get_local_player_code_status(player_code_dir)
if code_status.get('updated'):
code_status['updated_str'] = datetime.fromtimestamp(
code_status['updated']).strftime('%Y-%m-%d %H:%M:%S')
return render_template(
'admin/build_player.html',
settings=settings,
code_status=code_status,
player_code_dir=player_code_dir,
)
@admin_bp.route('/build-player', methods=['POST'])
@login_required
@admin_required
def build_player_action():
"""Build/refresh the staged player code and/or write its base config."""
from app.utils.player_build import (
build_player_files, write_base_config, get_short_head,
save_build_settings, make_build_record,
)
player_code_dir = current_app.config['PLAYER_CODE_DIR']
action = request.form.get('action', 'build_and_config')
repo_url = request.form.get('repo_url', '').strip()
branch = request.form.get('branch', 'main').strip() or 'main'
server_ip = request.form.get('server_ip', '').strip()
port = request.form.get('port', '443').strip()
use_https = request.form.get('use_https') == 'on'
verify_ssl = request.form.get('verify_ssl') == 'on'
orientation = request.form.get('orientation', 'Landscape').strip() or 'Landscape'
max_resolution = request.form.get('max_resolution', '1920x1080').strip() or '1920x1080'
# Validation
errors = []
if action in ('build_files', 'build_and_config') and not repo_url:
errors.append('Repository URL is required to build player files.')
if action in ('save_config', 'build_and_config') and not server_ip:
errors.append('Server IP / domain is required for the player configuration.')
try:
port_num = int(port)
if port_num < 1 or port_num > 65535:
errors.append('Port must be between 1 and 65535.')
except ValueError:
errors.append('Port must be a valid number.')
if errors:
for err in errors:
flash(err, 'warning')
return redirect(url_for('admin.build_player'))
messages = []
success = True
version = None
# Step 1: build/refresh files from the repository.
if action in ('build_files', 'build_and_config'):
result = build_player_files(player_code_dir, repo_url, branch)
version = result.get('version')
messages.append(result['message'])
if not result['success']:
success = False
log_action('error', f'Player build failed by {current_user.username}: {result["message"]}')
# Step 2: write the base config (only if the previous step didn't fail).
if success and action in ('save_config', 'build_and_config'):
cfg_result = write_base_config(
player_code_dir=player_code_dir,
server_ip=server_ip,
port=port,
use_https=use_https,
verify_ssl=verify_ssl,
orientation=orientation,
max_resolution=max_resolution,
)
messages.append(cfg_result['message'])
if not cfg_result['success']:
success = False
# Persist settings so deployment uses the same server address.
if version is None:
version = get_short_head(player_code_dir)
save_build_settings(
_player_build_meta_path(),
make_build_record(
repo_url=repo_url, branch=branch, server_ip=server_ip, port=port,
use_https=use_https, verify_ssl=verify_ssl, orientation=orientation,
max_resolution=max_resolution, version=version, built_by=current_user.username,
),
)
summary = ' '.join(messages) if messages else 'No action performed.'
if success:
log_action('info', f'Player files built by {current_user.username} (version {version})')
flash(f'{summary}', 'success')
else:
flash(f'⚠️ {summary}', 'danger')
return redirect(url_for('admin.build_player'))