Skip to content

Commit 46066a0

Browse files
authored
Merge pull request #26 from GYFX35/drinking-water-videos-page-16426890878590075587
Create drinking water videos page
2 parents 9a0304a + 8b0dde3 commit 46066a0

5 files changed

Lines changed: 195 additions & 0 deletions

File tree

eco_project/backend/app.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,56 @@ def videos():
519519
except (FileNotFoundError, json.JSONDecodeError):
520520
return jsonify([])
521521

522+
# Path for the drinking water videos JSON file and its lock file
523+
DRINKING_WATER_VIDEOS_FILE = os.path.join(app.root_path, 'drinking_water_videos.json')
524+
DRINKING_WATER_LOCK_FILE = os.path.join(app.root_path, 'drinking_water_videos.json.lock')
525+
526+
@app.route('/api/drinking_water_videos', methods=['GET', 'POST'])
527+
def drinking_water_videos():
528+
if request.method == 'POST':
529+
# Handle video submission
530+
data = request.get_json()
531+
title = data.get('title')
532+
url = data.get('url')
533+
534+
if not title or not url:
535+
return jsonify({"error": "Title and URL are required."}), 400
536+
537+
# Sanitize user input
538+
sanitized_title = bleach.clean(title)
539+
sanitized_url = bleach.clean(url)
540+
541+
# A simple check to ensure the URL is a YouTube embed URL
542+
if not sanitized_url.startswith("https://www.youtube.com/embed/"):
543+
return jsonify({"error": "Invalid YouTube URL."}), 400
544+
545+
with FileLock(DRINKING_WATER_LOCK_FILE):
546+
# Read existing videos
547+
try:
548+
with open(DRINKING_WATER_VIDEOS_FILE, 'r') as f:
549+
videos = json.load(f)
550+
except (FileNotFoundError, json.JSONDecodeError):
551+
videos = []
552+
553+
# Add new video
554+
videos.append({"title": sanitized_title, "url": sanitized_url})
555+
556+
# Write updated videos list back to the file
557+
with open(DRINKING_WATER_VIDEOS_FILE, 'w') as f:
558+
json.dump(videos, f, indent=4)
559+
560+
return jsonify({"message": "Video added successfully!"}), 201
561+
562+
else: # GET request
563+
# Return the list of videos
564+
with FileLock(DRINKING_WATER_LOCK_FILE):
565+
try:
566+
with open(DRINKING_WATER_VIDEOS_FILE, 'r') as f:
567+
videos = json.load(f)
568+
return jsonify(videos)
569+
except (FileNotFoundError, json.JSONDecodeError):
570+
return jsonify([])
571+
522572
@app.route('/api/chat', methods=['POST'])
523573
def chat():
524574
data = request.get_json()

eco_project/backend/drinking_water_videos.json.lock

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.video-container {
2+
display: flex;
3+
flex-wrap: wrap;
4+
justify-content: space-around;
5+
padding: 20px;
6+
}
7+
8+
.video-item {
9+
width: 300px;
10+
margin: 15px;
11+
border: 1px solid #ccc;
12+
box-shadow: 0 0 5px rgba(0,0,0,0.1);
13+
}
14+
15+
.video-item iframe {
16+
width: 100%;
17+
height: 170px;
18+
}
19+
20+
.video-item-title {
21+
padding: 10px;
22+
font-weight: bold;
23+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Drinking Water Videos - Environment Protection</title>
7+
<link rel="stylesheet" href="style.css">
8+
<link rel="stylesheet" href="drinking_water_videos.css">
9+
</head>
10+
<body>
11+
<header>
12+
<h1>Drinking Water Video Gallery</h1>
13+
<nav>
14+
<a href="index.html">Home</a>
15+
</nav>
16+
</header>
17+
<main>
18+
<section id="video-submission">
19+
<h2>Share a Video</h2>
20+
<form id="video-form">
21+
<input type="text" id="video-title" placeholder="Video Title" required>
22+
<input type="url" id="video-url" placeholder="Video URL" required>
23+
<button type="submit">Share Video</button>
24+
</form>
25+
<div id="error-message" class="error"></div>
26+
</section>
27+
<section id="videos">
28+
<h2>Featured Videos</h2>
29+
<div class="video-container">
30+
<!-- Video embeds will go here -->
31+
</div>
32+
</section>
33+
</main>
34+
<footer>
35+
<p>&copy; 2025 Environment Protection Initiative</p>
36+
</footer>
37+
<script src="drinking_water_videos.js"></script>
38+
</body>
39+
</html>
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
document.addEventListener('DOMContentLoaded', () => {
2+
const videoContainer = document.querySelector('.video-container');
3+
const videoForm = document.getElementById('video-form');
4+
5+
async function fetchVideos() {
6+
try {
7+
const response = await fetch('/api/drinking_water_videos');
8+
if (!response.ok) {
9+
throw new Error(`HTTP error! status: ${response.status}`);
10+
}
11+
const videos = await response.json();
12+
displayVideos(videos);
13+
} catch (error) {
14+
videoContainer.innerHTML = `<p>Error fetching videos: ${error.message}</p>`;
15+
}
16+
}
17+
18+
function displayVideos(videos) {
19+
if (videos.length === 0) {
20+
videoContainer.innerHTML = '<p>No videos to display.</p>';
21+
return;
22+
}
23+
let html = '';
24+
videos.forEach(video => {
25+
const embedUrl = video.url.includes('youtube.com/watch?v=')
26+
? video.url.replace('watch?v=', 'embed/')
27+
: video.url;
28+
29+
html += `
30+
<div class="video-item">
31+
<iframe src="${embedUrl}" frameborder="0" allowfullscreen></iframe>
32+
<div class="video-item-title">${video.title}</div>
33+
</div>
34+
`;
35+
});
36+
videoContainer.innerHTML = html;
37+
}
38+
39+
videoForm.addEventListener('submit', async (event) => {
40+
event.preventDefault();
41+
42+
const titleInput = document.getElementById('video-title');
43+
const urlInput = document.getElementById('video-url');
44+
const errorMessage = document.getElementById('error-message');
45+
46+
errorMessage.textContent = '';
47+
48+
let videoUrl = urlInput.value;
49+
if (videoUrl.includes('youtube.com/watch?v=')) {
50+
videoUrl = videoUrl.replace('watch?v=', 'embed/');
51+
}
52+
53+
const newVideo = {
54+
title: titleInput.value,
55+
url: videoUrl,
56+
};
57+
58+
try {
59+
const response = await fetch('/api/drinking_water_videos', {
60+
method: 'POST',
61+
headers: {
62+
'Content-Type': 'application/json',
63+
},
64+
body: JSON.stringify(newVideo),
65+
});
66+
67+
if (!response.ok) {
68+
const errorData = await response.json();
69+
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
70+
}
71+
72+
titleInput.value = '';
73+
urlInput.value = '';
74+
75+
fetchVideos(); // Refresh the list of videos
76+
} catch (error) {
77+
console.error('Failed to submit video:', error);
78+
errorMessage.textContent = `Error: ${error.message}`;
79+
}
80+
});
81+
82+
fetchVideos();
83+
});

0 commit comments

Comments
 (0)