updated server
This commit is contained in:
@@ -1,67 +0,0 @@
|
||||
# Stage 1: Build stage
|
||||
FROM python:3.11-slim AS build
|
||||
|
||||
# Set the working directory in the container
|
||||
WORKDIR /app
|
||||
|
||||
# Install build tools and libraries
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
python3-dev \
|
||||
cargo \
|
||||
g++ \
|
||||
curl \
|
||||
libjpeg-dev \
|
||||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Upgrade pip to the latest version
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
# Copy the current directory contents into the container at /app
|
||||
COPY . /app
|
||||
|
||||
# Install any needed packages specified in requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Stage 2: Runtime stage
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set the working directory in the container
|
||||
WORKDIR /app
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
g++ \
|
||||
curl \
|
||||
libjpeg-dev \
|
||||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Rust
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Copy only the necessary files from the build stage
|
||||
COPY --from=build /app /app
|
||||
COPY --from=build /root/.cargo /root/.cargo
|
||||
|
||||
# Install Gunicorn and other Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
RUN pip install gunicorn
|
||||
RUN apt-get update && apt-get install -y libreoffice poppler-utils ffmpeg
|
||||
|
||||
# Make port 5000 available to the world outside this container
|
||||
EXPOSE 5000
|
||||
|
||||
# Run the clear_db script, then the initialization script, and then Gunicorn
|
||||
CMD ["sh", "-c", "python clear_db.py && python init_db.py && gunicorn -w 4 -b 0.0.0.0:5000 app:app"]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
53
app.py
53
app.py
@@ -62,19 +62,28 @@ def admin_required(f):
|
||||
def add_image_to_playlist(file, filename, duration, target_type, target_id):
|
||||
"""
|
||||
Save the image file and add it to the playlist database.
|
||||
Increment the playlist version for the player or group.
|
||||
"""
|
||||
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
# Only save if file does not already exist (prevents double-saving)
|
||||
if not os.path.exists(file_path):
|
||||
file.save(file_path)
|
||||
|
||||
if target_type == 'group':
|
||||
group = Group.query.get_or_404(target_id)
|
||||
for player in group.players:
|
||||
new_content = Content(file_name=filename, duration=duration, player_id=player.id)
|
||||
db.session.add(new_content)
|
||||
# Increment playlist version for the group
|
||||
group.playlist_version += 1
|
||||
elif target_type == 'player':
|
||||
player = Player.query.get_or_404(target_id)
|
||||
new_content = Content(file_name=filename, duration=duration, player_id=target_id)
|
||||
db.session.add(new_content)
|
||||
# Increment playlist version for the player
|
||||
player.playlist_version += 1
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def convert_ppt_to_images(input_file, output_folder):
|
||||
"""
|
||||
@@ -597,20 +606,36 @@ def clean_unused_files():
|
||||
|
||||
|
||||
@app.route('/api/playlists', methods=['GET'])
|
||||
def get_playlist():
|
||||
def get_playlists():
|
||||
hostname = request.args.get('hostname')
|
||||
quickconnect_code = request.args.get('quickconnect_code')
|
||||
|
||||
# Validate the parameters
|
||||
if not hostname or not quickconnect_code:
|
||||
return jsonify({'error': 'Hostname and quick connect code are required'}), 400
|
||||
|
||||
# Find the player by hostname and verify the quickconnect code
|
||||
player = Player.query.filter_by(hostname=hostname).first()
|
||||
if not player or not player.verify_quickconnect_code(quickconnect_code):
|
||||
if not player or not bcrypt.check_password_hash(player.quickconnect_password, quickconnect_code):
|
||||
return jsonify({'error': 'Invalid hostname or quick connect code'}), 404
|
||||
|
||||
# Query the Content table for media files associated with the player
|
||||
content = Content.query.filter_by(player_id=player.id).all()
|
||||
playlist = [
|
||||
{
|
||||
'file_name': media.file_name,
|
||||
'url': f"http://{request.host}/media/{media.file_name}",
|
||||
'duration': media.duration
|
||||
}
|
||||
for media in content
|
||||
]
|
||||
|
||||
playlist = [{'file_name': item.file_name, 'duration': item.duration, 'url': url_for('media', filename=item.file_name, _external=True)} for item in content]
|
||||
return jsonify({'playlist': playlist})
|
||||
# Return the playlist, version, and hashed quickconnect code
|
||||
return jsonify({
|
||||
'playlist': playlist,
|
||||
'playlist_version': player.playlist_version,
|
||||
'hashed_quickconnect': player.quickconnect_password
|
||||
})
|
||||
|
||||
@app.route('/media/<path:filename>')
|
||||
def media(filename):
|
||||
@@ -720,6 +745,26 @@ def delete_group_media(group_id, content_id):
|
||||
db.session.commit()
|
||||
return redirect(url_for('manage_group', group_id=group_id))
|
||||
|
||||
@app.route('/api/playlist_version', methods=['GET'])
|
||||
def get_playlist_version():
|
||||
hostname = request.args.get('hostname')
|
||||
quickconnect_code = request.args.get('quickconnect_code')
|
||||
|
||||
# Validate the parameters
|
||||
if not hostname or not quickconnect_code:
|
||||
return jsonify({'error': 'Hostname and quick connect code are required'}), 400
|
||||
|
||||
# Find the player by hostname and verify the quickconnect code
|
||||
player = Player.query.filter_by(hostname=hostname).first()
|
||||
if not player or not bcrypt.check_password_hash(player.quickconnect_password, quickconnect_code):
|
||||
return jsonify({'error': 'Invalid hostname or quick connect code'}), 404
|
||||
|
||||
# Return the playlist version and hashed quickconnect code
|
||||
return jsonify({
|
||||
'playlist_version': player.playlist_version,
|
||||
'hashed_quickconnect': player.quickconnect_password
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
Binary file not shown.
10
models.py
10
models.py
@@ -12,10 +12,11 @@ class Content(db.Model):
|
||||
|
||||
class Player(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
hostname = db.Column(db.String(120), unique=True, nullable=False)
|
||||
password = db.Column(db.String(120), nullable=False)
|
||||
quickconnect_password = db.Column(db.String(120), nullable=False)
|
||||
username = db.Column(db.String(100), nullable=False, unique=True)
|
||||
hostname = db.Column(db.String(100), nullable=False)
|
||||
password = db.Column(db.String(200), nullable=False)
|
||||
quickconnect_password = db.Column(db.String(200), nullable=True) # Add this field
|
||||
playlist_version = db.Column(db.Integer, default=0) # Playlist version counter
|
||||
|
||||
def verify_quickconnect_code(self, code):
|
||||
return bcrypt.check_password_hash(self.quickconnect_password, code)
|
||||
@@ -52,6 +53,7 @@ class Group(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), nullable=False, unique=True)
|
||||
players = db.relationship('Player', secondary='group_player', backref='groups')
|
||||
playlist_version = db.Column(db.Integer, default=0) # Playlist version counter
|
||||
|
||||
# Association table for many-to-many relationship between Group and Player
|
||||
group_player = db.Table('group_player',
|
||||
|
||||
BIN
static/uploads/delete.png
Normal file
BIN
static/uploads/delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/uploads/edit_pencil.png
Normal file
BIN
static/uploads/edit_pencil.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
48
test_api.py
48
test_api.py
@@ -3,10 +3,10 @@ import os
|
||||
|
||||
# Replace with the actual server IP address or domain name, hostname, and quick connect code
|
||||
server_ip = 'http://localhost:5000'
|
||||
hostname = 'tv1'
|
||||
quickconnect_code = '4321'
|
||||
hostname = 'rpi-tv11'
|
||||
quickconnect_code = '8887779'
|
||||
|
||||
# Construct the URL with the hostname and quick connect code as query parameters
|
||||
# Construct the URL for the playlist API
|
||||
url = f'{server_ip}/api/playlists'
|
||||
params = {
|
||||
'hostname': hostname,
|
||||
@@ -24,21 +24,37 @@ print(f'Response Content: {response.text}')
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
# Parse the JSON response
|
||||
playlist = response.json().get('playlist', [])
|
||||
print('Playlist:', playlist)
|
||||
response_data = response.json()
|
||||
playlist = response_data.get('playlist', [])
|
||||
playlist_version = response_data.get('playlist_version', None)
|
||||
|
||||
print(f'Playlist Version: {playlist_version}')
|
||||
print(f'Playlist: {playlist}')
|
||||
|
||||
# Define the local folder for saving files
|
||||
local_folder = './static/resurse'
|
||||
if not os.path.exists(local_folder):
|
||||
os.makedirs(local_folder)
|
||||
|
||||
# Download each file in the playlist
|
||||
for item in playlist:
|
||||
file_url = item['url']
|
||||
file_name = item['file_name']
|
||||
response = requests.get(file_url)
|
||||
if response.status_code == 200:
|
||||
with open(file_name, 'wb') as f:
|
||||
f.write(response.content)
|
||||
print(f'Downloaded {file_name}')
|
||||
else:
|
||||
print(f'Failed to download {file_name}: {response.status_code}')
|
||||
for media in playlist:
|
||||
file_name = media.get('file_name', '')
|
||||
file_url = media.get('url', '')
|
||||
duration = media.get('duration', 10) # Default duration if not provided
|
||||
local_file_path = os.path.join(local_folder, file_name)
|
||||
|
||||
print(f'Downloading {file_name} from {file_url}...')
|
||||
try:
|
||||
file_response = requests.get(file_url, timeout=10)
|
||||
if file_response.status_code == 200:
|
||||
with open(local_file_path, 'wb') as file:
|
||||
file.write(file_response.content)
|
||||
print(f'Successfully downloaded {file_name} to {local_file_path}')
|
||||
else:
|
||||
print(f'Failed to download {file_name}. Status Code: {file_response.status_code}')
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f'Error downloading {file_name}: {e}')
|
||||
except requests.exceptions.JSONDecodeError as e:
|
||||
print(f'Failed to parse JSON response: {e}')
|
||||
else:
|
||||
print('Failed to retrieve playlist:', response.text)
|
||||
print(f'Failed to retrieve playlist. Status Code: {response.status_code}')
|
||||
Reference in New Issue
Block a user