From 99226f7fffca710c1441e58f4df2a492d507ca42 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 11:50:54 +0000 Subject: [PATCH] feat: Enable video sharing on the platform This commit introduces a new feature that allows users to share videos about the project topics. Key changes include: - A new video submission form on the videos page. - A new backend endpoint to handle video submissions. - A file-based persistent storage solution for videos. - Input sanitization to prevent XSS vulnerabilities. - File locking to prevent race conditions. - Improved frontend error handling. --- .gitignore | 2 + eco_project/backend/app.py | 59 +++++++++++++++++++++++--- eco_project/backend/static/videos.html | 9 ++++ eco_project/backend/static/videos.js | 56 +++++++++++++++++++++++- eco_project/backend/videos.json | 14 ++++++ eco_project/backend/videos.json.lock | 0 6 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 eco_project/backend/videos.json create mode 100644 eco_project/backend/videos.json.lock diff --git a/.gitignore b/.gitignore index d9590c2..20016f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pycache__/ eco_project/backend/gcloud-credentials.json server.log +eco_project/backend/videos.json +eco_project/backend/videos.json.lock diff --git a/eco_project/backend/app.py b/eco_project/backend/app.py index 04f6d17..7c6eaae 100644 --- a/eco_project/backend/app.py +++ b/eco_project/backend/app.py @@ -465,14 +465,59 @@ def livestock(): ] return jsonify(mock_data) -@app.route('/api/videos') +import json +import bleach +from filelock import FileLock + +# Path for the videos JSON file and its lock file +VIDEOS_FILE = os.path.join(app.root_path, 'videos.json') +LOCK_FILE = os.path.join(app.root_path, 'videos.json.lock') + +@app.route('/api/videos', methods=['GET', 'POST']) def videos(): - mock_videos = [ - {"title": "The Problem with Traditional Agriculture", "url": "https://www.youtube.com/embed/Yp7XFAE8kr4"}, - {"title": "Agroecology for Sustainable Food Systems", "url": "https://www.youtube.com/embed/6OyGlwYUS5w"}, - {"title": "How does an organic farmer conserve water?", "url": "https://www.youtube.com/embed/32ZMYDbItQ8"} - ] - return jsonify(mock_videos) + if request.method == 'POST': + # Handle video submission + data = request.get_json() + title = data.get('title') + url = data.get('url') + + if not title or not url: + return jsonify({"error": "Title and URL are required."}), 400 + + # Sanitize user input + sanitized_title = bleach.clean(title) + sanitized_url = bleach.clean(url) + + # A simple check to ensure the URL is a YouTube embed URL + if not sanitized_url.startswith("https://www.youtube.com/embed/"): + return jsonify({"error": "Invalid YouTube URL."}), 400 + + with FileLock(LOCK_FILE): + # Read existing videos + try: + with open(VIDEOS_FILE, 'r') as f: + videos = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + videos = [] + + # Add new video + videos.append({"title": sanitized_title, "url": sanitized_url}) + + # Write updated videos list back to the file + with open(VIDEOS_FILE, 'w') as f: + json.dump(videos, f, indent=4) + + return jsonify({"message": "Video added successfully!"}), 201 + + else: # GET request + # Return the list of videos + with FileLock(LOCK_FILE): + try: + with open(VIDEOS_FILE, 'r') as f: + videos = json.load(f) + return jsonify(videos) + except (FileNotFoundError, json.JSONDecodeError): + return jsonify([]) @app.route('/api/chat', methods=['POST']) def chat(): diff --git a/eco_project/backend/static/videos.html b/eco_project/backend/static/videos.html index 216a61a..a3a3993 100644 --- a/eco_project/backend/static/videos.html +++ b/eco_project/backend/static/videos.html @@ -15,6 +15,15 @@

Video Gallery

+
+

Share a Video

+
+ + + +
+
+

Featured Videos

diff --git a/eco_project/backend/static/videos.js b/eco_project/backend/static/videos.js index 9fd5e51..55a8861 100644 --- a/eco_project/backend/static/videos.js +++ b/eco_project/backend/static/videos.js @@ -1,5 +1,6 @@ document.addEventListener('DOMContentLoaded', () => { const videoContainer = document.querySelector('.video-container'); + const videoForm = document.getElementById('video-form'); async function fetchVideos() { try { @@ -15,11 +16,19 @@ document.addEventListener('DOMContentLoaded', () => { } function displayVideos(videos) { + if (videos.length === 0) { + videoContainer.innerHTML = '

No videos to display.

'; + return; + } let html = ''; videos.forEach(video => { + const embedUrl = video.url.includes('youtube.com/watch?v=') + ? video.url.replace('watch?v=', 'embed/') + : video.url; + html += `
- +
${video.title}
`; @@ -27,5 +36,48 @@ document.addEventListener('DOMContentLoaded', () => { videoContainer.innerHTML = html; } + videoForm.addEventListener('submit', async (event) => { + event.preventDefault(); + + const titleInput = document.getElementById('video-title'); + const urlInput = document.getElementById('video-url'); + const errorMessage = document.getElementById('error-message'); + + errorMessage.textContent = ''; + + let videoUrl = urlInput.value; + if (videoUrl.includes('youtube.com/watch?v=')) { + videoUrl = videoUrl.replace('watch?v=', 'embed/'); + } + + const newVideo = { + title: titleInput.value, + url: videoUrl, + }; + + try { + const response = await fetch('/api/videos', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(newVideo), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || `HTTP error! status: ${response.status}`); + } + + titleInput.value = ''; + urlInput.value = ''; + + fetchVideos(); // Refresh the list of videos + } catch (error) { + console.error('Failed to submit video:', error); + errorMessage.textContent = `Error: ${error.message}`; + } + }); + fetchVideos(); -}); \ No newline at end of file +}); diff --git a/eco_project/backend/videos.json b/eco_project/backend/videos.json new file mode 100644 index 0000000..3cd647c --- /dev/null +++ b/eco_project/backend/videos.json @@ -0,0 +1,14 @@ +[ + { + "title": "The Problem with Traditional Agriculture", + "url": "https://www.youtube.com/embed/Yp7XFAE8kr4" + }, + { + "title": "Agroecology for Sustainable Food Systems", + "url": "https://www.youtube.com/embed/6OyGlwYUS5w" + }, + { + "title": "How does an organic farmer conserve water?", + "url": "https://www.youtube.com/embed/32ZMYDbItQ8" + } +] diff --git a/eco_project/backend/videos.json.lock b/eco_project/backend/videos.json.lock new file mode 100644 index 0000000..e69de29