|
'use strict' |
|
|
|
// CONFIGURE YOUR SETTINGS HERE |
|
// Letterboxd username |
|
const LETTERBOXD_USERNAME = 'oan' |
|
|
|
// Which page should the script start on? |
|
// (if the importer fails somewhere) |
|
const LETTERBOXD_START_PAGE = 0 |
|
|
|
// Taste.io Authorization token |
|
// Too lazy to impl auth considering my account is facebook oauth'd |
|
const TOKEN = '' |
|
|
|
// Maps letterboxd 10 point rating scale to taste.io 4 point scale. |
|
// Adjust if desired |
|
const mapRating = function(rating) { |
|
// 1 2 3 4 5 6 7 8, 9, 10 |
|
return [null, 1, 1, 1, 2, 2, 3, 3, 4, 4, 4][rating] |
|
} |
|
|
|
// Change to true if ratings should be updated |
|
const UPDATE_RATINGS = false |
|
|
|
// ================================================================= |
|
// DOING A CODE HERE DON'T BE LOOKIN DOWN THERE LESS U LOOKIN FOR IT |
|
|
|
const through2 = require('through2') |
|
const JSONStream = require('JSONStream') |
|
const got = require('got') |
|
const cookie = require('cookie') |
|
const Xray = require('x-ray') |
|
|
|
const x = Xray() |
|
|
|
const serializeCookie = function(obj) { |
|
return Object.keys(obj).map((key) => cookie.serialize(key, obj[key])).join('; ') |
|
} |
|
|
|
// Scrape my letterboxd watchlist |
|
const pageParam = (LETTERBOXD_START_PAGE > 0) ? `page/${LETTERBOXD_START_PAGE}/` : '' |
|
const url = `http://letterboxd.com/${LETTERBOXD_USERNAME}/films/${pageParam}` |
|
x(url, '.film-list', x('.poster-container', [{ |
|
rating: '@data-owner-rating', |
|
title: '.film-poster img@alt', |
|
}])) |
|
.paginate('.paginate-nextprev .next@href') |
|
.write() |
|
// Stream the scraped titles one by one |
|
.pipe(JSONStream.parse('*')) |
|
.pipe(through2.obj(function({title, rating}, enc, cb){ |
|
if(rating == 0) { |
|
console.log(`Skipping ${title}`) |
|
return cb(null) |
|
} |
|
|
|
console.log(`Rating ${title} as ${rating} -> ${mapRating(rating)}...`) |
|
|
|
got('https://www.taste.io/api/movies/search?q=' + encodeURIComponent(title), { headers: { |
|
Authorization: 'Bearer ' + TOKEN |
|
}, json: true }) |
|
|
|
.then(res => { |
|
if(!res.body.movies.length) return |
|
const movie = res.body.movies[0] |
|
if(!UPDATE_RATINGS && movie.user && movie.user.rating) { |
|
console.log(`Skipping ${title} (already rated)`) |
|
return |
|
} |
|
|
|
const cook = cookie.parse(res.headers['set-cookie'].join('; ')) |
|
|
|
return new Promise(resolve => { |
|
const req = got.post('https://www.taste.io/api/movies/' + movie.slug + '/rating', { |
|
headers: { |
|
Authorization: 'Bearer ' + TOKEN, |
|
Cookie: serializeCookie(cook), |
|
'X-XSRF-TOKEN': cook['XSRF-TOKEN'] |
|
}, |
|
body: { |
|
rating: mapRating(rating) |
|
}, |
|
json: true |
|
}).then(res => resolve(res)).catch(err => console.error(err, req.body)) |
|
}) |
|
}) |
|
|
|
.then(_ => { |
|
cb(null, 'Success!\n') |
|
}) |
|
.catch(err => { console.error(err); cb(err) }) |
|
})) |
|
|
|
// Pipe results to stdout! |
|
.pipe(process.stdout) |
|
.on('error', err => console.error(err)) |
@pedromga It should work if the Taste API or Letterboxd structure is not changed. Tell me if it has and I will update the script when I can.
FYI, the Taste API search is a bit clunky sometimes and selects the wrong title in some cases. Currently the script is just selecting the first title in the search result. It could be interesting to check if the year is correct, and if not look at the next result.