Add HTML report generation for missing albums
- Introduced a new `html_report.py` file to generate an HTML report for albums to add and update. - Implemented a `generate_html_report` function that creates a styled HTML document with clickable submission links. - Integrated the new report generation function into `main.py` to streamline the process of reporting missing albums. - Enhanced user experience with filtering options for album types and artists in the generated report.
This commit is contained in:
357
html_report.py
Normal file
357
html_report.py
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
"""HTML report generation for missing albums"""
|
||||||
|
|
||||||
|
from html import escape
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
def generate_html_report(albums_to_add: List[Dict], albums_to_update: List[Dict]):
|
||||||
|
"""Generate an HTML report with clickable submission links"""
|
||||||
|
html_content = """<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MusicBrainz Albums - Add & Update</title>
|
||||||
|
<style>
|
||||||
|
body {{
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}}
|
||||||
|
h1 {{
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 3px solid #4CAF50;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}}
|
||||||
|
h2 {{
|
||||||
|
color: #2196F3;
|
||||||
|
margin-top: 30px;
|
||||||
|
border-bottom: 2px solid #2196F3;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}}
|
||||||
|
.album {{
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}}
|
||||||
|
.album-title {{
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2196F3;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}}
|
||||||
|
.artist-name {{
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}}
|
||||||
|
.links {{
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}}
|
||||||
|
.link-button {{
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}}
|
||||||
|
.link-button:hover {{
|
||||||
|
background-color: #45a049;
|
||||||
|
}}
|
||||||
|
.link-button.atisket {{
|
||||||
|
background-color: #2196F3;
|
||||||
|
}}
|
||||||
|
.link-button.atisket:hover {{
|
||||||
|
background-color: #0b7dda;
|
||||||
|
}}
|
||||||
|
.link-button.harmony {{
|
||||||
|
background-color: #FF9800;
|
||||||
|
}}
|
||||||
|
.link-button.harmony:hover {{
|
||||||
|
background-color: #e68900;
|
||||||
|
}}
|
||||||
|
.deezer-link {{
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-top: 10px;
|
||||||
|
}}
|
||||||
|
.mb-link {{
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-top: 5px;
|
||||||
|
}}
|
||||||
|
.issues {{
|
||||||
|
color: #FF9800;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-top: 5px;
|
||||||
|
font-style: italic;
|
||||||
|
}}
|
||||||
|
.summary {{
|
||||||
|
background: white;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}}
|
||||||
|
.filter-buttons {{
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}}
|
||||||
|
.filter-button {{
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: 2px solid #2196F3;
|
||||||
|
background-color: white;
|
||||||
|
color: #2196F3;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1em;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}}
|
||||||
|
.filter-button:hover {{
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
}}
|
||||||
|
.filter-button.active {{
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
}}
|
||||||
|
.album-section {{
|
||||||
|
display: none;
|
||||||
|
}}
|
||||||
|
.album-section.visible {{
|
||||||
|
display: block;
|
||||||
|
}}
|
||||||
|
.filter-container {{
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}}
|
||||||
|
.filter-group {{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}}
|
||||||
|
.filter-group label {{
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}}
|
||||||
|
.artist-buttons {{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}}
|
||||||
|
.artist-button {{
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: white;
|
||||||
|
color: #666;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9em;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}}
|
||||||
|
.artist-button:hover {{
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-color: #2196F3;
|
||||||
|
}}
|
||||||
|
.artist-button.active {{
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
border-color: #2196F3;
|
||||||
|
}}
|
||||||
|
.album[data-artist] {{
|
||||||
|
display: block;
|
||||||
|
}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>🎵 MusicBrainz Albums - Add & Update</h1>
|
||||||
|
<div class="summary">
|
||||||
|
<strong>Albums to ADD: {add_count}</strong> | <strong>Albums to UPDATE: {update_count}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="filter-container">
|
||||||
|
<div class="filter-group">
|
||||||
|
<label>Filter by Type:</label>
|
||||||
|
<div class="filter-buttons">
|
||||||
|
<button class="filter-button active" data-filter="all">Show All</button>
|
||||||
|
<button class="filter-button" data-filter="add">To ADD ({add_count})</button>
|
||||||
|
<button class="filter-button" data-filter="update">To UPDATE ({update_count})</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="filter-group">
|
||||||
|
<label>Filter by Artist:</label>
|
||||||
|
<div class="artist-buttons">
|
||||||
|
<button class="artist-button active" data-artist="all">All Artists</button>
|
||||||
|
{artist_buttons}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {{
|
||||||
|
const typeButtons = document.querySelectorAll('.filter-button');
|
||||||
|
const artistButtons = document.querySelectorAll('.artist-button');
|
||||||
|
const addSection = document.getElementById('albums-to-add');
|
||||||
|
const updateSection = document.getElementById('albums-to-update');
|
||||||
|
|
||||||
|
let currentTypeFilter = 'all';
|
||||||
|
let currentArtistFilter = 'all';
|
||||||
|
|
||||||
|
function applyFilters() {{
|
||||||
|
const albums = document.querySelectorAll('.album');
|
||||||
|
|
||||||
|
albums.forEach(album => {{
|
||||||
|
const albumArtist = album.getAttribute('data-artist');
|
||||||
|
const isInAddSection = addSection && addSection.contains(album);
|
||||||
|
const isInUpdateSection = updateSection && updateSection.contains(album);
|
||||||
|
|
||||||
|
let showByType = false;
|
||||||
|
if (currentTypeFilter === 'all') {{
|
||||||
|
showByType = true;
|
||||||
|
}} else if (currentTypeFilter === 'add' && isInAddSection) {{
|
||||||
|
showByType = true;
|
||||||
|
}} else if (currentTypeFilter === 'update' && isInUpdateSection) {{
|
||||||
|
showByType = true;
|
||||||
|
}}
|
||||||
|
|
||||||
|
const showByArtist = currentArtistFilter === 'all' || albumArtist === currentArtistFilter;
|
||||||
|
|
||||||
|
if (showByType && showByArtist) {{
|
||||||
|
album.style.display = 'block';
|
||||||
|
}} else {{
|
||||||
|
album.style.display = 'none';
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
|
||||||
|
if (addSection) {{
|
||||||
|
const hasVisibleAlbums = Array.from(addSection.querySelectorAll('.album'))
|
||||||
|
.some(album => album.style.display !== 'none');
|
||||||
|
addSection.style.display = hasVisibleAlbums ? 'block' : 'none';
|
||||||
|
}}
|
||||||
|
|
||||||
|
if (updateSection) {{
|
||||||
|
const hasVisibleAlbums = Array.from(updateSection.querySelectorAll('.album'))
|
||||||
|
.some(album => album.style.display !== 'none');
|
||||||
|
updateSection.style.display = hasVisibleAlbums ? 'block' : 'none';
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
typeButtons.forEach(button => {{
|
||||||
|
button.addEventListener('click', function() {{
|
||||||
|
typeButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
|
this.classList.add('active');
|
||||||
|
currentTypeFilter = this.getAttribute('data-filter');
|
||||||
|
applyFilters();
|
||||||
|
}});
|
||||||
|
}});
|
||||||
|
|
||||||
|
artistButtons.forEach(button => {{
|
||||||
|
button.addEventListener('click', function() {{
|
||||||
|
artistButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
|
this.classList.add('active');
|
||||||
|
currentArtistFilter = this.getAttribute('data-artist');
|
||||||
|
applyFilters();
|
||||||
|
}});
|
||||||
|
}});
|
||||||
|
}});
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
|
||||||
|
album_html = """
|
||||||
|
<div class="album" data-artist="{artist_escaped}">
|
||||||
|
<div class="album-title">{title}</div>
|
||||||
|
<div class="artist-name">by {artist}</div>
|
||||||
|
{mb_info}
|
||||||
|
{issues_info}
|
||||||
|
<div class="links">
|
||||||
|
<a href="{atisket_link}" target="_blank" class="link-button atisket">Submit via a-tisket</a>
|
||||||
|
<a href="{harmony_link}" target="_blank" class="link-button harmony">Submit via Harmony</a>
|
||||||
|
</div>
|
||||||
|
<div class="deezer-link">
|
||||||
|
<a href="{deezer_url}" target="_blank">View on Deezer</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def format_album(album: Dict, is_update: bool = False) -> str:
|
||||||
|
submission_links = album.get("submission_links", {})
|
||||||
|
mb_info = ""
|
||||||
|
issues_info = ""
|
||||||
|
|
||||||
|
if is_update:
|
||||||
|
mb_url = album.get("mb_url", "")
|
||||||
|
if mb_url:
|
||||||
|
mb_info = f'<div class="mb-link"><a href="{mb_url}" target="_blank">View on MusicBrainz</a></div>'
|
||||||
|
issues = album.get("album_issues", [])
|
||||||
|
if issues:
|
||||||
|
issues_info = f'<div class="issues">Issues: {", ".join(issues)}</div>'
|
||||||
|
|
||||||
|
title = album.get("title", "Unknown Title")
|
||||||
|
artist = album.get("artist_name", "Unknown Artist")
|
||||||
|
artist_escaped = escape(artist)
|
||||||
|
atisket_link = submission_links.get("atisket_link", "#")
|
||||||
|
harmony_link = submission_links.get("harmony_link", "#")
|
||||||
|
deezer_url = submission_links.get("deezer_url", "#")
|
||||||
|
|
||||||
|
return album_html.format(
|
||||||
|
title=escape(title),
|
||||||
|
artist=artist,
|
||||||
|
artist_escaped=artist_escaped,
|
||||||
|
mb_info=mb_info,
|
||||||
|
issues_info=issues_info,
|
||||||
|
atisket_link=atisket_link,
|
||||||
|
harmony_link=harmony_link,
|
||||||
|
deezer_url=deezer_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
all_albums = albums_to_add + albums_to_update
|
||||||
|
unique_artists = sorted(
|
||||||
|
set(album.get("artist_name", "Unknown") for album in all_albums)
|
||||||
|
)
|
||||||
|
|
||||||
|
artist_buttons_html = "".join(
|
||||||
|
f'<button class="artist-button" data-artist="{escape(artist)}">{escape(artist)}</button>'
|
||||||
|
for artist in unique_artists
|
||||||
|
)
|
||||||
|
|
||||||
|
albums_html = ""
|
||||||
|
if albums_to_add:
|
||||||
|
albums_html += '<div id="albums-to-add" class="album-section visible">'
|
||||||
|
albums_html += "<h2>📥 Albums to ADD (Not in MusicBrainz)</h2>"
|
||||||
|
formatted_add = map(lambda album: format_album(album, False), albums_to_add)
|
||||||
|
albums_html += "".join(formatted_add)
|
||||||
|
albums_html += "</div>"
|
||||||
|
|
||||||
|
if albums_to_update:
|
||||||
|
albums_html += '<div id="albums-to-update" class="album-section visible">'
|
||||||
|
albums_html += "<h2>🔄 Albums to UPDATE (Need Linking/Updates)</h2>"
|
||||||
|
formatted_update = map(
|
||||||
|
lambda album: format_album(album, True), albums_to_update
|
||||||
|
)
|
||||||
|
albums_html += "".join(formatted_update)
|
||||||
|
albums_html += "</div>"
|
||||||
|
|
||||||
|
add_count = len(albums_to_add)
|
||||||
|
update_count = len(albums_to_update)
|
||||||
|
html_header = html_content.format(
|
||||||
|
add_count=add_count,
|
||||||
|
update_count=update_count,
|
||||||
|
artist_buttons=artist_buttons_html,
|
||||||
|
)
|
||||||
|
html_footer = "\n</body>\n</html>\n"
|
||||||
|
html_content = html_header + albums_html + html_footer
|
||||||
|
|
||||||
|
with open("missing_albums.html", "w", encoding="utf-8") as f:
|
||||||
|
f.write(html_content)
|
||||||
|
print(f"📄 HTML report saved to missing_albums.html")
|
||||||
176
main.py
176
main.py
@@ -13,6 +13,8 @@ from urllib.parse import quote
|
|||||||
import requests
|
import requests
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
from html_report import generate_html_report
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
@@ -342,179 +344,5 @@ def main():
|
|||||||
generate_html_report(all_albums_to_add, all_albums_to_update)
|
generate_html_report(all_albums_to_add, all_albums_to_update)
|
||||||
|
|
||||||
|
|
||||||
def generate_html_report(albums_to_add: List[Dict], albums_to_update: List[Dict]):
|
|
||||||
"""Generate an HTML report with clickable submission links"""
|
|
||||||
html_content = """<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>MusicBrainz Albums - Add & Update</title>
|
|
||||||
<style>
|
|
||||||
body {{
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}}
|
|
||||||
h1 {{
|
|
||||||
color: #333;
|
|
||||||
border-bottom: 3px solid #4CAF50;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}}
|
|
||||||
h2 {{
|
|
||||||
color: #2196F3;
|
|
||||||
margin-top: 30px;
|
|
||||||
border-bottom: 2px solid #2196F3;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}}
|
|
||||||
.album {{
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 20px 0;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
}}
|
|
||||||
.album-title {{
|
|
||||||
font-size: 1.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #2196F3;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}}
|
|
||||||
.artist-name {{
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}}
|
|
||||||
.links {{
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}}
|
|
||||||
.link-button {{
|
|
||||||
display: inline-block;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background-color: #4CAF50;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
transition: background-color 0.3s;
|
|
||||||
}}
|
|
||||||
.link-button:hover {{
|
|
||||||
background-color: #45a049;
|
|
||||||
}}
|
|
||||||
.link-button.atisket {{
|
|
||||||
background-color: #2196F3;
|
|
||||||
}}
|
|
||||||
.link-button.atisket:hover {{
|
|
||||||
background-color: #0b7dda;
|
|
||||||
}}
|
|
||||||
.link-button.harmony {{
|
|
||||||
background-color: #FF9800;
|
|
||||||
}}
|
|
||||||
.link-button.harmony:hover {{
|
|
||||||
background-color: #e68900;
|
|
||||||
}}
|
|
||||||
.deezer-link {{
|
|
||||||
color: #666;
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-top: 10px;
|
|
||||||
}}
|
|
||||||
.mb-link {{
|
|
||||||
color: #666;
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-top: 5px;
|
|
||||||
}}
|
|
||||||
.issues {{
|
|
||||||
color: #FF9800;
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-top: 5px;
|
|
||||||
font-style: italic;
|
|
||||||
}}
|
|
||||||
.summary {{
|
|
||||||
background: white;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
}}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>🎵 MusicBrainz Albums - Add & Update</h1>
|
|
||||||
<div class="summary">
|
|
||||||
<strong>Albums to ADD: {add_count}</strong> | <strong>Albums to UPDATE: {update_count}</strong>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
album_html = """
|
|
||||||
<div class="album">
|
|
||||||
<div class="album-title">{title}</div>
|
|
||||||
<div class="artist-name">by {artist}</div>
|
|
||||||
{mb_info}
|
|
||||||
{issues_info}
|
|
||||||
<div class="links">
|
|
||||||
<a href="{atisket_link}" target="_blank" class="link-button atisket">Submit via a-tisket</a>
|
|
||||||
<a href="{harmony_link}" target="_blank" class="link-button harmony">Submit via Harmony</a>
|
|
||||||
</div>
|
|
||||||
<div class="deezer-link">
|
|
||||||
<a href="{deezer_url}" target="_blank">View on Deezer</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
def format_album(album: Dict, is_update: bool = False) -> str:
|
|
||||||
submission_links = album.get("submission_links", {})
|
|
||||||
mb_info = ""
|
|
||||||
issues_info = ""
|
|
||||||
|
|
||||||
if is_update:
|
|
||||||
mb_url = album.get("mb_url", "")
|
|
||||||
if mb_url:
|
|
||||||
mb_info = f'<div class="mb-link"><a href="{mb_url}" target="_blank">View on MusicBrainz</a></div>'
|
|
||||||
issues = album.get("album_issues", [])
|
|
||||||
if issues:
|
|
||||||
issues_info = f'<div class="issues">Issues: {", ".join(issues)}</div>'
|
|
||||||
|
|
||||||
title = album.get("title", "Unknown Title")
|
|
||||||
artist = album.get("artist_name", "Unknown Artist")
|
|
||||||
atisket_link = submission_links.get("atisket_link", "#")
|
|
||||||
harmony_link = submission_links.get("harmony_link", "#")
|
|
||||||
deezer_url = submission_links.get("deezer_url", "#")
|
|
||||||
|
|
||||||
return album_html.format(
|
|
||||||
title=title,
|
|
||||||
artist=artist,
|
|
||||||
mb_info=mb_info,
|
|
||||||
issues_info=issues_info,
|
|
||||||
atisket_link=atisket_link,
|
|
||||||
harmony_link=harmony_link,
|
|
||||||
deezer_url=deezer_url,
|
|
||||||
)
|
|
||||||
|
|
||||||
albums_html = ""
|
|
||||||
if albums_to_add:
|
|
||||||
albums_html += "<h2>📥 Albums to ADD (Not in MusicBrainz)</h2>"
|
|
||||||
formatted_add = map(lambda album: format_album(album, False), albums_to_add)
|
|
||||||
albums_html += "".join(formatted_add)
|
|
||||||
|
|
||||||
if albums_to_update:
|
|
||||||
albums_html += "<h2>🔄 Albums to UPDATE (Need Linking/Updates)</h2>"
|
|
||||||
formatted_update = map(
|
|
||||||
lambda album: format_album(album, True), albums_to_update
|
|
||||||
)
|
|
||||||
albums_html += "".join(formatted_update)
|
|
||||||
|
|
||||||
add_count = len(albums_to_add)
|
|
||||||
update_count = len(albums_to_update)
|
|
||||||
html_header = html_content.format(add_count=add_count, update_count=update_count)
|
|
||||||
html_footer = "\n</body>\n</html>\n"
|
|
||||||
html_content = html_header + albums_html + html_footer
|
|
||||||
|
|
||||||
with open("missing_albums.html", "w", encoding="utf-8") as f:
|
|
||||||
f.write(html_content)
|
|
||||||
print(f"📄 HTML report saved to missing_albums.html")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user