При добавлении ссылки с soundcloud в профиле возникает ошибка: You have not provided a valid SoundCloud URL. Learn more about using SoundCloud players (это сообщение в самом плеере SoundCloud).

ссылка такого типа: https://soundcloud.com/ty-segall-official/my-ladys-on-fire?si=eeb0108b59c14736ad1f38f2383ffb95&utm_source=clipboard&utm_medium=text&utm_campaign=social_sharing

app.js:

const express = require(“express”);
const fs = require(“fs”);
const session = require(“express-session”);
const fileUpload = require(“express-fileupload”);
const app = express();


app.set(“view engine”, “ejs”);
app.use(express.static(“public”));
app.use(express.urlencoded({ extended: true }));
app.use(fileUpload());
app.use(session({
secret: “mysecretkey”,
resave: false,
saveUninitialized: false
}));

const predefinedGenres = [‘Rock’, ‘Pop’, ‘Jazz’, ‘Hip Hop’, ‘Electronic’, ‘Blues’];

function getMusicianById(id) {
const data = fs.readFileSync(“./db/musicians.json”);
const musicians = JSON.parse(data);

return musicians.musicians.find(musician => musician.id === id);
}

function requireLogin(req, res, next) {
if (req.session.musicianId) {
next();
} else {
res.redirect(“/login”);
}
}

function search(query) {
const data = fs.readFileSync(‘./db/musicians.json’);
const musicians = JSON.parse(data).musicians.map(musician => {
return {
name: musician.name,
genre: musician.genre,
originalName: musician.name,
profileLink: /profile/${musician.id},
thumbnail: musician.thumbnail,
soundcloud: musician.soundcloud
};
});
let results = musicians;
if (query) {
const lowerQuery = query.toLowerCase();
results = musicians.filter(musician => {
const nameScore = musician.name.toLowerCase().startsWith(lowerQuery) ? 2 : musician.name.toLowerCase().includes(lowerQuery) ? 1 : 0;
const genreScore = musician.genre.toLowerCase().startsWith(lowerQuery) ? 2 : musician.genre.toLowerCase().includes(lowerQuery) ? 1 : 0;
return nameScore + genreScore > 0;
}).sort((a, b) => {
const aNameScore = a.name.toLowerCase().startsWith(lowerQuery) ? 2 : a.name.toLowerCase().includes(lowerQuery) ? 1 : 0;
const bNameScore = b.name.toLowerCase().startsWith(lowerQuery) ? 2 : b.name.toLowerCase().includes(lowerQuery) ? 1 : 0;
const aGenreScore = a.genre.toLowerCase().startsWith(lowerQuery) ? 2 : a.genre.toLowerCase().includes(lowerQuery) ? 1 : 0;
const bGenreScore = b.genre.toLowerCase().startsWith(lowerQuery) ? 2 : b.genre.toLowerCase().includes(lowerQuery) ? 1 : 0;
return (bNameScore + bGenreScore) - (aNameScore + aGenreScore);
});
}
return results;
}

app.use((req, res, next) => {
if (req.session.musicianId) {
const musician = getMusicianById(req.session.musicianId);
res.locals.musician = musician;
res.locals.userLoggedIn = true;
res.locals.username = musician.name;
} else {
res.locals.userLoggedIn = false;
}

next();
});

app.get(“/”, (req, res) => {
const data = fs.readFileSync(“./db/musicians.json”);
const musicians = JSON.parse(data);

res.render(“index”, { musicians: musicians.musicians });
});

app.get(“/register”, (req, res) => {
if (req.session.musicianId) {
const musician = getMusicianById(req.session.musicianId);
res.redirect(“/profile/” + musician.id);
} else {
res.render(“register”);
}
});

app.post(“/register”, (req, res) => {
if (req.session.musicianId) {
const musician = getMusicianById(req.session.musicianId);
res.redirect(“/profile/” + musician.id);
} else {
const data = fs.readFileSync(“./db/musicians.json”);
const musicians = JSON.parse(data);

const newMusician = {
id: musicians.musicians.length + 1,
name: req.body.name,
genre: req.body.genre,
instrument: req.body.instrument,
soundcloud: req.body.soundcloud,
password: req.body.password,
location: req.body.location,
login: req.body.login
};

if (req.files && req.files.thumbnail) {
const file = req.files.thumbnail;
const filename = “musician_” + newMusician.id + “" + file.name;
file.mv(“./public/img/” + filename);

newMusician.thumbnail = filename;
}

musicians.musicians.push(newMusician);
fs.writeFileSync(“./db/musicians.json”, JSON.stringify(musicians));

req.session.musicianId = newMusician.id;
res.redirect(“/profile/” + newMusician.id);
}
});

app.get(“/profile/:id”, (req, res) => {
const musician = getMusicianById(parseInt(req.params.id));

if (musician) {
res.render(“profile”, { musician: musician });
} else {
res.status(404).send(“Musician not found”);
}
});

app.get(“/login”, (req, res) => {
res.render(“login”);
});

app.post(“/login”, (req, res) => {
const data = fs.readFileSync(“./db/musicians.json”);
const musicians = JSON.parse(data);

const musician = musicians.musicians.find(musician => musician.login === req.body.login && musician.password === req.body.password);

if (musician) {
req.session.musicianId = musician.id;
res.redirect(“/profile/” + musician.id);

} else {
res.render(“login”, { error: “Invalid login or password” });
}
});

app.get(“/logout”, (req, res) => {
req.session.destroy();
res.redirect(“/”);
});

app.get(‘/search’, (req, res) => {
const query = req.query.query || ‘’;
const musicians = search(query);
res.locals.predefinedGenres = predefinedGenres;
res.render(‘search’, { musicians, query });
});

app.get(“/profile/:id/edit”, requireLogin, (req, res) => {
const musician = getMusicianById(parseInt(req.params.id));

if (musician) {
if (req.session.musicianId === musician.id) { // Check if the logged-in user is the owner of the profile
res.render(“edit-profile”, { musician: musician });
} else {
res.status(403).send(“Access denied”);
}
} else {
res.status(404).send(“Musician not found”);
}
});





app.post(‘/profile/:id/edit’, requireLogin, (req, res) => {
const musician = getMusicianById(parseInt(req.params.id));

if (musician) {
if (!req.body.name || !req.body.genre) {
res.status(400).send(‘Please fill out all fields’);

} else {
musician.name = req.body.name;
musician.genre = req.body.genre;
musician.instrument = req.body.instrument;
musician.soundcloud = req.body.soundcloud;
musician.location = req.body.location;
musician.bio = req.body.bio;

if (req.files && req.files.thumbnail) {
const file = req.files.thumbnail;
const filename = 'musician’ + musician.id + ‘_’ + file.name;
file.mv(‘./public/img/’ + filename);
musician.thumbnail = filename;
}

const data = fs.readFileSync(‘./db/musicians.json’);
const musicians = JSON.parse(data);
const index = musicians.musicians.findIndex(m => m.id === musician.id);
musicians.musicians[index] = musician;

fs.writeFileSync(‘./db/musicians.json’, JSON.stringify(musicians));

res.redirect(‘/profile/’ + musician.id);
}
} else {
res.status(404).send(‘Musician not found’);
}
});

function isValidSoundCloudUrl(url) {
return url.startsWith(‘https://soundcloud.com/’);
}

app.listen(3000, () => {
console.log(“Server started on port 3000”);
});

profile.ejs:

<!DOCTYPE html>
<html>
<head>
<title><%= musician.name %> - Musician Profile</title>
</head>
<body>
<img src=”/img/<%= musician.thumbnail %>" alt=“<%= musician.name %>”>
<h1><%= musician.name %></h1>
<p><strong>Genre:</strong> <%= musician.genre %></p>
<p><strong>Instrument:</strong> <%= musician.instrument %></p>
<p><strong>Location:</strong> <%= musician.location %></p>
<p><strong>Bio:</strong> <%= musician.bio %></p>

<iframe width=“100%” height=“300” scrolling=“no” frameborder=“no” src=“https://w.soundcloud.com/player/?url=<%= musician.soundcloud %>&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true”></iframe>

<% if (userLoggedIn && username === musician.name) { %>
<a href=“/profile/<%= musician.id %>/edit”>Edit profile</a>

<div id=“edit-profile-modal” class=“modal”>
<div class=“modal-content”>
<span class=“close”>×</span>
<h2>Edit Profile</h2>
<form action=“/profile/<%= musician.id %>/edit” method=“POST” enctype=“multipart/form-data”>
<div>
<label for=“name”>Name:</label>
<input type=“text” id=“name” name=“name” value=“<%= musician.name %>”>
</div>
<div>
<label for=“genre”>Genre:</label>
<input type=“text” id=“genre” name=“genre” value=“<%= musician.genre %>”>
</div>
<div>
<label for=“instrument”>Instrument:</label>
<input type=“text” id=“instrument” name=“instrument” value=“<%= musician.instrument %>”>
</div>
<div>
<label for=“location”>Location:</label>
<input type=“text” id=“location” name=“location” value=“<%= musician.location %>”>
</div>
<div>
<label for=“bio”>Bio:</label>
<textarea id=“bio” name=“bio”><%= musician.bio %></textarea>
</div>

<div>
<label for=“soundcloud”>SoundCloud:</label>
<div id=“music-fields”>
<% if (musician.soundcloud) { %>
<% if (Array.isArray(musician.soundcloud)) { musician.soundcloud.forEach(function(url) { %>
<div>
<input type=“text” name=“soundcloud[]” value=“<%= url %>”>
<button type=“button” class=“delete-music-button”>×</button>
</div>
<% }); %> <!-- Add this closing tag -->
<% } %>
<% } %>
<div>
<input type=“text” name=“soundcloud[]” placeholder=“SoundCloud track URL”>
<button type=“button” class=“add-music-button”>Add Music</button>
</div>
</div>
</div>

<div>
<label for=“thumbnail”>Thumbnail:</label>
<input type=“file” id=“thumbnail” name=“thumbnail”>
</div>

<button type=“submit”>Save</button>
</form>
</div>
</div>
<% } %>

<script>
const modal = document.getElementById(“edit-profile-modal”);
const btn = document.getElementsByTagName(“a”)[0];
const span = document.getElementsByClassName(“close”)[0];

btn.onclick = function() {
modal.style.display = “block”;
}

span.onclick = function() {
modal.style.display = “none”;
}

window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = “none”;
}
}

const musicFields = document.getElementById(“music-fields”);
const addMusicButton = document.querySelector(“.add-music-button”);

addMusicButton.addEventListener(“click”, (event) => {
event.preventDefault();
const musicField = document.createElement(“div”);
musicField.innerHTML = ‘<br/> <input type=“text” name=“soundcloud[]” placeholder=“SoundCloud track URL”><br/> <button type=“button” class=“delete-music-button”>×</button><br/>’;
musicFields.insertBefore(musicField, addMusicButton.parentNode);
});

musicFields.addEventListener(“click”, (event) => {
if (event.target.classList.contains(“delete-music-button”)) {
event.preventDefault();
event.target.parentNode.remove();
}
});
</script>
</body>
</html>