diff --git a/README.md b/README.md index 65d2452..5831ffd 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ npm install letterboxd --save ## usage -### function(username, [callback]) +### function(username, [format], [callback]) Returns a promise if no callback is provided. @@ -26,6 +26,18 @@ letterboxd("rubencordeiro") .catch((error) => console.log(error)); ``` +#### format + +Pass “markdown” as the optional `format` parameter for Markdown output in each film’s review. + +```javascript +import letterboxd from "letterboxd"; + +letterboxd("rubencordeiro", "markdown") + .then((items) => console.log(items)) + .catch((error) => console.log(error)); +``` + #### output output is an array of items. @@ -50,7 +62,7 @@ items of note for the list type: image: { tiny: "...", small: "...", medium: "...", large: "..." }, }, rating: { text: "★★★★", score: 4 }, - review: "proper cute, funny and interesting through out. ...", + review: "proper cute, funny and interesting through out. Co-directed by Byron Howard, who also directed Tangled.", spoilers: false, isRewatch: false, date: { watched: 1463702400000, published: 1463784779000 }, @@ -84,3 +96,25 @@ items of note for the list type: //... ]; ``` + +If “markdown” is passed as the format parameter then HTML elements in the review text will be converted to Markdown: + +```javascript +[ + { + type: "diary", + film: { + title: "Zootopia", + year: "2016", + image: { tiny: "...", small: "...", medium: "...", large: "..." }, + }, + rating: { text: "★★★★", score: 4 }, + review: "_proper_ cute, funny and interesting through out. Co-directed by Byron Howard, who also directed [Tangled](https://letterboxd.com/film/tangled-2010/).", + spoilers: false, + isRewatch: false, + date: { watched: 1463702400000, published: 1463784779000 }, + uri: "https://letterboxd.com/zoetrope69/film/zootopia/", + }, + //... +``` + diff --git a/package.json b/package.json index 958e5f5..10657dc 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "license": "ISC", "dependencies": { "cheerio": "^1.0.0-rc.10", - "node-fetch": "^2.6.6" + "node-fetch": "^2.6.6", + "turndown": "^7.1.2" }, "devDependencies": { "cross-env": "7.0.3", diff --git a/src/letterboxd.js b/src/letterboxd.js index 015afca..801fae6 100644 --- a/src/letterboxd.js +++ b/src/letterboxd.js @@ -1,5 +1,6 @@ import fetch from "node-fetch"; import cheerio from "cheerio"; +import TurndownService from 'turndown'; function isListItem(element) { // if the list path is in the url @@ -132,6 +133,33 @@ function getReview(element) { return review; } +function getReviewAsMarkdown(element) { + const description = element.find("description").text(); + + const $ = cheerio.load(description); + + const reviewParagraphs = $("p").slice(1); + + let review = ""; + + // if there is no review return the item + if (reviewParagraphs.length <= 0) { + return review; + } + + // the rest of description is a review, if there is no review the string 'Watched on ' will appear + // this assumes you didn't write the 'Watched on ' string in your review... weak + if (reviewParagraphs.last().text().includes("Watched on ")) { + return review; + } + + review = reviewParagraphs.toString(); + const turndownService = new TurndownService(); + const reviewMarkdown = turndownService.turndown(review); + + return reviewMarkdown; +} + function getListFilms(element) { const description = element.find("description").text(); const $ = cheerio.load(description); @@ -220,7 +248,7 @@ function isListRanked(element) { return isOrderedListPresent; } -function processItem(element) { +function processItem(element, reviewFormat) { // there are two types of items: lists and diary entries if (isListItem(element)) { @@ -252,7 +280,7 @@ function processItem(element) { image: getImage(element), }, rating: getRating(element), - review: getReview(element), + review: reviewFormat == 'markdown' ? getReviewAsMarkdown(element): getReview(element), spoilers: getSpoilers(element), isRewatch: getIsRewatch(element), uri: getUri(element), @@ -263,7 +291,7 @@ function invalidUsername(username) { return !username || username.trim().length <= 0; } -function getDiaryData(username) { +function getDiaryData(username, reviewFormat) { const uri = `https://letterboxd.com/${username}/rss/`; return fetch(uri) @@ -285,20 +313,20 @@ function getDiaryData(username) { const items = []; $("item").each((i, element) => { - items[i] = processItem($(element)); + items[i] = processItem($(element), reviewFormat); }); return items; }); } -function letterboxd(username) { +function letterboxd(username, reviewFormat) { // check if a valid username has been passed in if (invalidUsername(username)) { return Promise.reject(new Error("No username sent as a parameter")); } - return getDiaryData(username); + return getDiaryData(username, reviewFormat); } export default letterboxd;