initial v1

This commit is contained in:
Volker
2025-12-25 22:32:24 +01:00
commit f2cb420182
18 changed files with 933 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MusicBrainz RSS Feeder</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #fdfdfd;
margin: 0;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
header h1 {
font-size: 24px;
margin: 0;
}
header h1 a {
text-decoration: none;
color: #333;
}
nav a {
margin-left: 20px;
text-decoration: none;
color: #555;
}
h2, h3 {
border-bottom: 1px solid #eee;
padding-bottom: 10px;
margin-top: 40px;
}
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 10px 0;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
li:last-child {
border-bottom: none;
}
form {
margin-top: 20px;
}
input[type="text"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background-color: #333;
color: white;
padding: 8px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #555;
}
.form-inline {
display: inline;
margin-top: 0;
}
.item-actions {
display: flex;
align-items: center;
gap: 10px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1><a href="{{ url_for('index') }}">MusicBrainz RSS Feeder</a></h1>
<nav>
<a href="{{ url_for('index') }}">Feeds</a>
<a href="{{ url_for('settings') }}">Settings</a>
</nav>
</header>
{% block content %}{% endblock %}
</div>
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
{% extends "base.html" %}
{% block content %}
<h2>Edit Feed: {{ feed.name }}</h2>
<h3>Artists in this feed</h3>
<ul>
{% for artist in feed.artists %}
<li>
{{ artist.name }}
<div class="item-actions">
<form class="form-inline" action="{{ url_for('remove_artist', feed_id=feed.id, artist_id=artist.id) }}" method="post">
<button type="submit">Remove</button>
</form>
</div>
</li>
{% else %}
<li>No artists in this feed.</li>
{% endfor %}
</ul>
<h3>Add Artist</h3>
<form onsubmit="return false;">
<input type="text" id="artist-search" placeholder="Search for an artist...">
</form>
<div id="search-results"></div>
<script>
const searchInput = document.getElementById('artist-search');
const resultsDiv = document.getElementById('search-results');
let searchTimeout;
searchInput.addEventListener('input', () => {
clearTimeout(searchTimeout);
const query = searchInput.value;
if (query.length < 3) {
resultsDiv.innerHTML = '';
return;
}
searchTimeout = setTimeout(() => {
fetch(`{{ url_for('search_artist') }}?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(artists => {
resultsDiv.innerHTML = '<h4>Search Results</h4>';
if (artists.length === 0) {
resultsDiv.innerHTML += '<p>No artists found.</p>';
return;
}
const list = document.createElement('ul');
artists.forEach(artist => {
const item = document.createElement('li');
item.innerHTML = `
<div>
${artist.name} (${artist.country || 'N/A'})
</div>
<div class="item-actions">
<form class="form-inline" action="{{ url_for('add_artist', feed_id=feed.id) }}" method="post">
<input type="hidden" name="artist_id" value="${artist.id}">
<input type="hidden" name="artist_name" value="${artist.name}">
<button type="submit">Add</button>
</form>
</div>
`;
list.appendChild(item);
});
resultsDiv.appendChild(list);
});
}, 300);
});
</script>
{% endblock %}
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ feed.name }}</title>
<link>{{ request.url_root }}</link>
<description>Latest album releases for artists in the '{{ feed.name }}' feed.</description>
<language>en</language>
<lastBuildDate>{{ last_build_date }}</lastBuildDate>
<atom:link href="{{ request.url }}" rel="self" type="application/rss+xml" />
{% for release in releases %}
<item>
<title>{{ release.artist.name }} - {{ release.title }}</title>
<link>https://musicbrainz.org/release/{{ release.id }}</link>
<guid isPermaLink="false">{{ release.id }}</guid>
<description><![CDATA[
<p><b>{{ release.artist.name }}</b> <i>{{ release.title }}</i><br>
Release Date: {{ release.date }}</p>
{% if release.hasCoverArt %}
<img src="http://coverartarchive.org/release/{{ release.id }}/front-500" alt="Cover Art {{ release.title }}">
{% endif %}
]]></description>
{% if release.pub_date %}
<pubDate>{{ release.pub_date }}</pubDate>
{% endif %}
</item>
{% endfor %}
</channel>
</rss>
+31
View File
@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block content %}
<h2>Feeds</h2>
<ul>
{% for feed in feeds %}
<li>
<div>
<a href="{{ url_for('edit_feed', feed_id=feed.id) }}">{{ feed.name }}</a>
({{ feed.artists|length }})
</div>
<div class="item-actions">
<a href="{{ url_for('get_feed_rss', feed_id=feed.id) }}">RSS</a>
<form class="form-inline" action="{{ url_for('delete_feed', feed_id=feed.id) }}" method="post">
<button type="submit">Delete</button>
</form>
</div>
</li>
{% else %}
<li>No feeds configured.</li>
{% endfor %}
</ul>
<h3>Create New Feed</h3>
<form action="{{ url_for('create_feed') }}" method="post">
<label for="name">Feed Name:</label>
<input type="text" id="name" name="name" required>
<button type="submit">Create</button>
</form>
{% endblock %}
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0">
<head>
<title>MusicBrainz Artist Feeds</title>
</head>
<body>
<outline text="MusicBrainz Artist Feeds" title="MusicBrainz Artist Feeds">
{% for feed in feeds %}
<outline type="rss" text="{{ (feed.artists | map(attribute='name') | sort | join(', ')) }}" title="{{ feed.name }} ({{ feed.artists | length }} artists)" xmlUrl="{{ url_for('get_feed_rss', feed_id=feed.id, _external=True) }}"/>
{% endfor %}
</outline>
</body>
</opml>
+19
View File
@@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
<h2>Settings</h2>
<form method="post">
<div>
<label for="days_back">Days to look back for releases:</label>
<input type="number" id="days_back" name="days_back" value="{{ settings.days_back }}" required>
</div>
<br>
<div>
<label for="cache_time_hours">Cache time (hours):</label>
<input type="number" id="cache_time_hours" name="cache_time_hours" value="{{ settings.cache_time_hours }}" required>
</div>
<br>
<button type="submit">Save Settings</button>
</form>
{% endblock %}