114 lines
4.9 KiB
HTML
114 lines
4.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>OPML-to-BlogRoll</title>
|
|
<style>
|
|
body { font-family: -apple-system, sans-serif; background-color: #f4f7f9; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
|
|
.card { background: white; padding: 2rem; border-radius: 15px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); width: 100%; max-width: 450px; text-align: center; }
|
|
h1 { color: #2c3e50; font-size: 1.5rem; margin-bottom: 1.5rem; }
|
|
.field { margin-bottom: 1rem; text-align: left; }
|
|
label { display: block; margin-bottom: 5px; font-weight: bold; color: #555; }
|
|
input[type="file"], select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 8px; box-sizing: border-box; }
|
|
button { background: #0366d6; color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer; width: 100%; font-size: 1rem; font-weight: bold; margin-top: 10px; transition: background 0.3s; }
|
|
button:hover { background: #0255b3; }
|
|
footer { margin-top: 1.5rem; font-size: 0.8rem; color: #888; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="card">
|
|
<h1>OPML-to-BlogRoll</h1>
|
|
|
|
<div class="field">
|
|
<label for="fileInput">1. Selecciona archivo (OPML o XML):</label>
|
|
<input type="file" id="fileInput" accept=".opml,.xml">
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label for="formatSelect">2. Formato de salida:</label>
|
|
<select id="formatSelect">
|
|
<option value="html">HTML (con CSS Inline para WordPress)</option>
|
|
<option value="markdown">Markdown (Tablas escapadas)</option>
|
|
<option value="json">JSON (Datos puros)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button onclick="processFile()">Convertir y Descargar</button>
|
|
|
|
<footer>Versión Web Estática - No necesita instalacion</footer>
|
|
</div>
|
|
|
|
<script>
|
|
function escaparMarkdown(text) {
|
|
if (!text) return "";
|
|
return text.replace(/\|/g, "\\|").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
|
|
}
|
|
|
|
async function processFile() {
|
|
const fileInput = document.getElementById('fileInput');
|
|
const format = document.getElementById('formatSelect').value;
|
|
|
|
if (fileInput.files.length === 0) {
|
|
alert("Por favor, selecciona un archivo primero.");
|
|
return;
|
|
}
|
|
|
|
const file = fileInput.files[0];
|
|
const text = await file.text();
|
|
const parser = new DOMParser();
|
|
const xmlDoc = parser.parseFromString(text, "text/xml");
|
|
|
|
// Buscar todos los nodos 'outline' que tengan xmlUrl
|
|
const outlines = xmlDoc.querySelectorAll("outline[xmlUrl]");
|
|
const feeds = Array.from(outlines).map(el => ({
|
|
titulo: el.getAttribute("title") || el.getAttribute("text") || "Sin título",
|
|
url_web: el.getAttribute("htmlUrl") || "#",
|
|
url_rss: el.getAttribute("xmlUrl") || "#"
|
|
}));
|
|
|
|
let output = "";
|
|
let filename = file.name.split('.').slice(0, -1).join('.');
|
|
|
|
if (format === "html") {
|
|
output = '<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; font-family: sans-serif; margin: 20px 0;">';
|
|
feeds.forEach(f => {
|
|
output += `
|
|
<div style="border: 1px solid #e1e4e8; border-radius: 12px; padding: 15px; display: flex; justify-content: space-between; align-items: center; background: #ffffff; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
|
<a href="${f.url_web}" style="font-weight: bold; color: #0366d6; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;" target="_blank">${f.titulo}</a>
|
|
<a href="${f.url_rss}" style="background: #f68140; color: #ffffff !important; padding: 5px 10px; border-radius: 6px; font-size: 12px; text-decoration: none; font-weight: bold; margin-left: 12px;" target="_blank">RSS</a>
|
|
</div>`;
|
|
});
|
|
output += '\n</div>';
|
|
filename += ".html";
|
|
}
|
|
else if (format === "markdown") {
|
|
output = "| Sitio Web | Feed RSS |\n| :--- | :---: |\n";
|
|
feeds.forEach(f => {
|
|
output += `| [${escaparMarkdown(f.titulo)}](${f.url_web}) | [RSS](${f.url_rss}) |\n`;
|
|
});
|
|
filename += ".md";
|
|
}
|
|
else if (format === "json") {
|
|
output = JSON.stringify(feeds, null, 4);
|
|
filename += ".json";
|
|
}
|
|
|
|
downloadFile(output, filename);
|
|
}
|
|
|
|
function downloadFile(content, filename) {
|
|
const blob = new Blob([content], { type: "text/plain" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|