Skip to content

Commit

Permalink
First init
Browse files Browse the repository at this point in the history
  • Loading branch information
pahlevikun committed Dec 10, 2020
0 parents commit be33f6a
Show file tree
Hide file tree
Showing 8 changed files with 740 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/*
package-lock.json
*.log
21 changes: 21 additions & 0 deletions api/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const axios = require("axios");
const mediumURL =
"https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@";

const getUserData = async (username) => {
try {
const result = await axios.get(mediumURL + username);
const filteredResult = result.data.items.filter(
(item) =>
(!item.thumbnail.includes("stat?event") ||
!item.thumbnail.includes("&referrerSource")) &&
item.categories.length > 0
);
return filteredResult;
} catch (error) {
console.error(error);
return error;
}
};

module.exports = { getUserData };
102 changes: 102 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require("./src/config");
const { asyncForEach } = require("./src/utils");
const mediumCard = require("./src/card");
const { getUserData } = require("./api/api");
var express = require("express");
var app = express();
app.use(express.json());

app.get("/latest", async (request, response) => {
try {
if (!request.query.username) {
response.write(
JSON.stringify({
error: "username is required",
})
);
response.end();
return;
}

const username = request.query.username;
console.log(request.query);
const offset = request.query.offset || 0;
const width = request.query.width || config.card.width;
const height = request.query.height || config.card.height;
const limit = request.query.limit == null ? 1 :
request.query.limit <= 10
? request.query.limit
: false || config.default.limit;

request.query.width = width;
request.query.height = height;

var resultData = await getUserData(username);
let result = `<svg>`;

if (resultData.length < limit) limit = resultData.length;

result = `<svg xmlns="http:https://www.w3.org/2000/svg" xmlns:xlink="http:https://www.w3.org/1999/xlink"
width="${
(limit == 1 ? width : 2 * width) +
config.default.margin_left +
config.card.spacing
}"
version="1.2"
height="${
Math.round(limit / 2) * height +
config.default.margin_top * 2 +
config.card.spacing * Math.floor(limit / 2)
}"
viewBox="0 0 ${
(limit == 1 ? width : 2 * width) +
config.default.margin_left +
config.card.spacing
} ${
Math.round(limit / 2) * height +
config.default.margin_top * 2 +
config.card.spacing * Math.floor(limit / 2)
}">`;
resultData = resultData.slice(offset, offset + limit);
await asyncForEach(
resultData,
request.query,
async (blog, index, settings) => {
if (index >= limit) {
return;
}
const mediumCardObj = await mediumCard(blog, settings, index);
result += `<g requiredFeatures="http:https://www.w3.org/Graphics/SVG/feature/1.2/#TextFlow" transform="translate(${
(index % 2 ? width + config.card.spacing : 0) +
config.default.margin_left
}, ${
Math.floor(index / 2) * height +
config.default.margin_top +
(index > 1 ? config.card.spacing * Math.floor(index / 2) : 0)
})">${mediumCardObj}</g>`;
}
);

result += `</svg>`;

response.setHeader(
"Cache-Control",
"public, no-cache, no-store, must-revalidate"
);
response.setHeader("Expires", "-1");
response.setHeader("Pragma", "no-cache");
response.writeHead(200, { "Content-Type": "image/svg+xml" });

response.write(result);
response.end();
} catch (error) {
console.log(error);
response.send("Error while fetching the data" + error);
}
});

var port = process.env.PORT || 3000;

app.listen(port, function () {
console.log("Server listening " + port);
});
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "github-read-medium-card",
"version": "1.0.0",
"description": "show medium on github readme",
"main": "app.js",
"scripts": {
"start": "node ./app.js",
"dev": "nodemon ./app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"axios": "^0.19.2",
"express": "^4.17.1",
"sharp": "^0.26.2"
}
}
167 changes: 167 additions & 0 deletions src/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
require("./config");
const { imgToDataURL, dateFormat } = require("./utils");

const mediumCard = async (data, settings, index) => {
const result = await imgToDataURL(data.thumbnail);
const blogImage = "data:image/png;base64," + result.toString("base64");
const blogDate = await dateFormat(data.pubDate);
const blogLink = data.link;

var selected_theme = config.themes.default;

if (settings.theme && config.themes[settings.theme])
selected_theme = settings.theme;

var border_width = config.card.border_width;
var border_radius = config.card.border_radius + "px";
var width = settings.width;
var height = settings.height;
var bg_color = settings.bg_color || config.themes[selected_theme].bg_color;

var image_mask = {
background: settings.image_background || config.card.image_mask.background,
height: settings.image_height || config.card.image_mask.height,
width: settings.image_width || config.card.image_mask.width,
x: settings.image_x || config.card.image_mask.x,
y: settings.image_y || config.card.image_mask.y,
};

var image = {
height: settings.image_height || config.card.image.height,
width: settings.image_width || config.card.image.width,
x: settings.image_x || config.card.image.x,
y: settings.image_y || config.card.image.y,
};

var title = {
color: settings.title_color || config.themes[selected_theme].title_color,
x: settings.title_x || config.card.title.x,
y: settings.title_y || config.card.title.y,
};

var subTitle = {
color: settings.author_color || config.themes[selected_theme].author_color,
x: settings.title_x || config.card.sub_title.x,
y: settings.title_y || config.card.sub_title.y,
font_size: settings.author_font_size || config.card.sub_title.font_size,
};

var author = {
color: settings.author_color || config.themes[selected_theme].author_color,
x: settings.author_x || config.card.author.x,
y: settings.author_y || config.card.author.y,
font_size: settings.author_font_size || config.card.author.font_size,
};

console.log(author.y);

var date = {
color: settings.date_color || config.themes[selected_theme].date_color,
x: settings.date_x || config.card.date.x,
y: settings.date_y || config.card.date.y,
font_size: settings.date_font_size || config.card.date.font_size,
};

bg_color = config.themes[selected_theme].bg_color;
border_color = config.themes[selected_theme].border_color;

var max_characters = 30;
var character_tracker = 0;
var array_holder = [];
var title_string = "";
var sub_title_string =data.description
.replace(/<h3>.*<\/h3>|<figcaption>.*<\/figcaption>|<[^>]*>/gm, '')
.substring(0, 35) + '...';
var word_array = data.title.split(" ");
var total_words = word_array.length;
var line_tracker = 0;
var max_lines = 2;

try {
word_array.forEach((word, index) => {
if (
word.length + character_tracker <=
max_characters - array_holder.length
) {
character_tracker += word.length;
array_holder.push(word);
if (total_words == index + 1) {
title_string += `<tspan x="0" dy="1.2em">${array_holder.join(
" "
)}</tspan>`;
}
} else {
line_tracker++;
title_string += `<tspan x="0" dy="1.2em">${
array_holder.join(" ") + (line_tracker == max_lines ? "..." : "")
}</tspan>`;
if (line_tracker == max_lines) throw "";
array_holder = [];
character_tracker = 0;
character_tracker += word.length;
array_holder.push(word);
if (total_words == index + 1) {
title_string += `<tspan x="0" dy="1.2em">${array_holder.join(
" "
)}</tspan>`;
}
}
});
} catch (_) {}

return `
<svg xmlns="http:https://www.w3.org/2000/svg" height="${height}px" width="${width}px">
<defs>
<!-- define lines for text lies on -->
<path id="blogName" d="M0,20 H235 M0,35 H235 M0,50 H240 M0,65 H235"> </path>
<path id="blogAuthor" d="M0,85 H230 "></path>
<path id="blogDescription" d="M0,55 H230 "></path>
<path id="blogDate" d="M0,100 H230 "></path>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:${bg_color};stop-opacity:1" />
<stop offset="100%" style="stop-color:${bg_color};stop-opacity:1" />
</linearGradient>
<linearGradient id="grad2" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:${image_mask.background};stop-opacity:1" />
<stop offset="100%" style="stop-color:${image_mask.background};stop-opacity:1" />
</linearGradient>
<clipPath id="clip">
<use xlink:href="#rect"/>
</clipPath>
<pattern id="img${index}" patternUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
<image xlink:href="${blogImage}" x="${image.x}" y="${image.y}" height="${image.height}px" width="${image.width}px" />
</pattern>
</defs>
<use xlink:href="#rect" stroke-width="2" stroke="black"/>
<a href="${blogLink}" target="_blank">
<rect id="rect" x="0" y="0" width="100%" height="100%" style="fill:url(#grad1);ry:${border_radius};stroke-opacity:${border_width};stroke:${border_color}"></rect>
<text transform="translate(${title.x},${title.y})" fill="${title.color}" font-size="15" font-family="'Segoe UI', Ubuntu, Sans-Serif" font-weight="bold">
${title_string}
</text>
<text transform="translate(${subTitle.x},${subTitle.y})" fill="${subTitle.color}" font-size="${subTitle.font_size}" font-family="'Segoe UI', Ubuntu, Sans-Serif" font-weight="bold">
<textPath xlink:href="#blogDescription">${sub_title_string}</textPath>
</text>
<text transform="translate(${author.x},${author.y})" fill="${author.color}" font-size="${author.font_size}" font-family="'Segoe UI', Ubuntu, Sans-Serif">
<textPath xlink:href="#blogAuthor">${data.author}</textPath>
</text>
<text transform="translate(${date.x},${date.y})" fill="${date.color}" font-size="${date.font_size}" font-family="'Segoe UI', Ubuntu, Sans-Serif">
<textPath xlink:href="#blogDate">${blogDate}</textPath>
</text>
<rect clip-path="url(#clip)" x="${image_mask.x}" y="${image_mask.y}" height="${image_mask.height}px" width="${image_mask.width}px" style="fill:url(#img${index});"></rect>
</a>
</svg>
`;
};

module.exports = mediumCard;
Loading

0 comments on commit be33f6a

Please sign in to comment.