-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit be33f6a
Showing
8 changed files
with
740 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules/* | ||
package-lock.json | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.