Artist Ripper working

This commit is contained in:
2026-09-18 23:45:26 -06:00
parent 86d8283217
commit b6d155f071
4 changed files with 403 additions and 169 deletions
+47
View File
@@ -0,0 +1,47 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Happy Ripper</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
crossorigin="anonymous"
/>
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">Happy Ripper</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNavAltMarkup"
aria-controls="navbarNavAltMarkup"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" aria-current="page" href="/">Queue</a>
<a class="nav-link" href="/browse">Browse</a>
</div>
</div>
</div>
</nav>
<section class="content">{% block content %}{% endblock %}</section>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"
></script>
</body>
</html>
+95
View File
@@ -0,0 +1,95 @@
{% extends 'base.html' %} {% block content %}
<div class="container">
<form id="browse-form">
<div class="mb-3">
<input
type="text"
name="search"
id="searchInput"
placeholder="Album, Arist, Playlist..."
class="form-control"
required
/>
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
</div>
</br>
<div class="container" id="results"></div>
<script>
async function enqueue(url, is_artist) {
const formData = new FormData();
formData.append("url", url);
if (is_artist) {
formData.append("is-channel-check", "");
}
const response = await fetch("/rip", {
method: "POST",
body: formData,
});
const data = await response.json();
}
const searchInput = document.getElementById("searchInput");
const results = document.getElementById("results");
document
.getElementById("browse-form")
.addEventListener("submit", async function (e) {
e.preventDefault();
const formData = new FormData(this);
const response = await fetch("/search", {
method: "POST",
body: formData,
});
const data = await response.json();
console.log(data)
results.innerHTML = "";
for (release of data) {
var button = "url" in release ? `
<div class="col">
<button class="btn btn-primary" onclick="enqueue('${release.url}', ${release.category == 'artist'})">
Enqueue
</button>
</div>` : "";
results.innerHTML += `
<div class="card" style="max-width: 720px;">
<div class="row">
<div class="col">
<img
src="${release.thumbnails[0].url}"
class="card-img-top"
style="max-width: 120px;"
>
</div>
<div class="col">
<div class="card-body">
<h5 class="card-title">${release.title}</h5>
<p class="card-text">${release.category} • ${release.artist}</p>
</div>
</div>
${button}
</div>
</div>
<br>
`;
}
});
</script>
{% endblock %}
+153 -168
View File
@@ -1,174 +1,159 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Happy Ripper</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
crossorigin="anonymous"
/>
</head>
<body>
<div class="container">
<h2>Request</h2>
<form id="rip-form">
<div class="mb-3">
<input
type="text"
name="url"
id="urlInput"
placeholder="https://music.youtube.com/..."
class="form-control"
required
/>
{% extends 'base.html' %} {% block content %}
<div class="container">
<h2>Request</h2>
<form id="rip-form">
<div class="mb-3">
<input
type="text"
name="url"
id="urlInput"
placeholder="https://music.youtube.com/..."
class="form-control"
required
/>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="is-channel-check" name="is-channel-check">
<label class="form-check-label" for="is-channel-check">Is Channel</label>
</div>
<button type="submit" class="btn btn-primary">Queue</button>
</form>
</div>
</br>
<div class="container">
<h2>Current</h2>
<div id="status-message"></div>
</div>
</br>
<div class="container">
<h2>Queue</h2>
<table class="table" id="queue-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">URL</th>
</tr>
</thead>
<tbody id="queue-table-body"></tbody>
</table>
</div>
</br>
<div class="container">
<h2>Finished</h2>
<div id=finish-table-body></div>
</div>
<script>
function createCard(artist, title, status, art) {
return `
<div class="card mb-3" style="max-width: 540px;">
<div class="row g-0">
<div class="col-md-4">
<img src="${art}" class="img-fluid rounded-start">
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${artist}</p>
<p class="card-text"><small class="text-body-secondary">${status}</small></p>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Queue</button>
</form>
</div>
</br>
<div class="container">
<h2>Current</h2>
<div id="status-message"></div>
</div>
</br>
<div class="container">
<h2>Queue</h2>
<table class="table" id="queue-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">URL</th>
</tr>
</thead>
<tbody id="queue-table-body"></tbody>
</table>
</div>
</br>
<div class="container">
<h2>Finished</h2>
<div id=finish-table-body></div>
</div>
<script>
function createCard(artist, title, status, art) {
return `
<div class="card mb-3" style="max-width: 540px;">
<div class="row g-0">
<div class="col-md-4">
<img src="${art}" class="img-fluid rounded-start">
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${artist}</p>
<p class="card-text"><small class="text-body-secondary">${status}</small></p>
</div>
`;
}
function createFinishCard(artist, title, status, art, destinations) {
let dstList = ""
for (destination of destinations) {
dstList += `<li>${destination}</li>`;
}
let extraClasses = ""
if (status !== "Finished sucessfully") {
extraClasses = "border-danger"
}
return `
<div class="card mb-3 ${extraClasses}" style="max-width: 540px;">
<div class="row g-0">
<div class="col-md-4">
<img src="${art}" class="img-fluid rounded-start">
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${artist}</p>
<ol>
${dstList}
</ol>
<p class="card-text"><small class="text-body-secondary">${status}</small></p>
</div>
</div>
</div>
`;
}
function createFinishCard(artist, title, status, art, destinations) {
let dstList = ""
for (destination of destinations) {
dstList += `<li>${destination}</li>`;
}
let extraClasses = ""
if (status !== "Finished sucessfully") {
extraClasses = "border-danger"
}
return `
<div class="card mb-3 ${extraClasses}" style="max-width: 540px;">
<div class="row g-0">
<div class="col-md-4">
<img src="${art}" class="img-fluid rounded-start">
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">${artist}</p>
<ol>
${dstList}
</ol>
<p class="card-text"><small class="text-body-secondary">${status}</small></p>
</div>
</div>
</div>
</div>
`;
}
const ripFormInput = document.getElementById("urlInput");
document
.getElementById("rip-form")
.addEventListener("submit", async function (e) {
e.preventDefault();
const formData = new FormData(this);
const response = await fetch("/rip", {
method: "POST",
body: formData,
});
const data = await response.json();
ripFormInput.value = "";
</div>
`;
}
const ripFormInput = document.getElementById("urlInput");
document
.getElementById("rip-form")
.addEventListener("submit", async function (e) {
e.preventDefault();
const formData = new FormData(this);
const response = await fetch("/rip", {
method: "POST",
body: formData,
});
const queueTableBody = document.getElementById("queue-table-body");
const finishTableBody = document.getElementById("finish-table-body");
const statusMessage = document.getElementById("status-message");
const statusMessageImage = document.getElementById("status-message-img");
async function status() {
const response = await fetch("/status", { method: "GET" });
const statusJson = await response.json();
if ("currentReleaseInfo" in statusJson) {
const currentInfo = statusJson.currentReleaseInfo;
statusMessage.innerHTML = createCard(currentInfo.artist, currentInfo.title, statusJson.status, currentInfo.art_url);
}
else {
statusMessage.innerHTML = `<p>${statusJson.status}</p>`;
}
queueTableBody.innerHTML = "";
var i = 1;
for (const url of statusJson.queue) {
queueTableBody.innerHTML += `<tr><td>${i}</td><td>${url}</td></tr>`;
i ++;
}
finishTableBody.innerHTML = "";
for (const q of statusJson.finishedQueue.toReversed()) {
finishTableBody.innerHTML += createFinishCard(q.artist, q.title, q.status, q.art_url, q.destinations);
}
const data = await response.json();
ripFormInput.value = "";
});
const queueTableBody = document.getElementById("queue-table-body");
const finishTableBody = document.getElementById("finish-table-body");
const statusMessage = document.getElementById("status-message");
const statusMessageImage = document.getElementById("status-message-img");
async function status() {
const response = await fetch("/status", { method: "GET" });
const statusJson = await response.json();
if ("currentReleaseInfo" in statusJson) {
const currentInfo = statusJson.currentReleaseInfo;
statusMessage.innerHTML = createCard(currentInfo.artist, currentInfo.title, statusJson.status, currentInfo.art_url);
}
setInterval(status, 100);
</script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"
></script>
</body>
</html>
else {
statusMessage.innerHTML = `<p>${statusJson.status}</p>`;
}
queueTableBody.innerHTML = "";
var i = 1;
for (const url of statusJson.queue) {
queueTableBody.innerHTML += `<tr><td>${i}</td><td>${url}</td></tr>`;
i ++;
}
finishTableBody.innerHTML = "";
for (const q of statusJson.finishedQueue.toReversed()) {
finishTableBody.innerHTML += createFinishCard(q.artist, q.title, q.status, q.art_url, q.destinations);
}
}
setInterval(status, 100);
</script>
{% endblock %}