Skip to content

Instantly share code, notes, and snippets.

@johanhermansson
Created August 7, 2019 07:04
Show Gist options
  • Save johanhermansson/aca79bd3d1171cbce50960065fa9ea74 to your computer and use it in GitHub Desktop.
Save johanhermansson/aca79bd3d1171cbce50960065fa9ea74 to your computer and use it in GitHub Desktop.
Import Letterboxd scores into Taste.io

Letterboxd -> Taste.io import script

This script imports your Letterboxd ratings into taste.io.

Lazy update of rahatarmanahmed Gist: https://gist.github.com/rahatarmanahmed/87acb61c9922ecec967380552d03d117

;)

Usage

Too lazy to make this into an npm package so here's how you could use it. You'll need nodejs and npm.

  1. Save the script
  2. Run npm i through2 JSONStream got cookie x-ray in the script's directory
  3. Login to taste.io and go to your home page. Open your browser's developer tools to the network tab. Refresh the page and look for the request to the /me endpoint. Copy the Authorization token from the headers.
  4. Edit the script at the top with your own letterboxd username and taste.io token.
  5. Run node letterboxd2taste.js
  6. cool ur done now
'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
Copy link

pedromga commented Oct 2, 2019

@johanhermansson !!! its importing! ... thank you os much for your time =) it´s gonna take a while necause its more than 4400 ratings, but i can even see the movies and shows that are missing on taste. ( example : "taste" , it didnt rate the right movie called taste due to the amount of movies with same name, maybe thats why most imports uses the imdb id and not names )
again, thank you i learned a bi with ur help :)

@johanhermansson
Copy link
Author

@pedromga, no. Don't think so. Your token has probably changed hmmm. Copy it again and try again. If you can see which film was rated last you can go to that page http://letterboxd.com/your-username/films/ and look for the page where it stopped and update the script with the new page number (the LETTERBOXD_START_PAGE variable)

If it doesn't work they probably does not like that many requests the same minute like that anymore 🤔

Maybe I can update the script in a few days with a automatic token updater

@pedromga
Copy link

pedromga commented Oct 2, 2019

yes, it changed, its working again =) i got DC from taste. its working : https://www.taste.io/users/PedroAbreu
it fails some ratings but that´s from the search that rates a diferent item, i can take care of that later. at least now i know how to run this scrips and i can look around github related themed sites scripts :)

@johanhermansson
Copy link
Author

@pedromga Awesome 👍

@pedromga
Copy link

pedromga commented Oct 2, 2019

i wonder if i can modify the script for CaptainWatch? the owner of that sie ( was called top50 before ) used to be able to import ratings, now hes having trouble to get it working

@pedromga
Copy link

pedromga commented Oct 2, 2019

@jlefebvre check it out 📦

@pedromgapt
Copy link

@johanhermansson
it stopped working :/

"Rating Line of Duty as 5 -> 2...
TypeError: The GET method cannot be used with a body
at Object.exports.normalizeRequestArguments (/home/runner/node_modules/got/dist/source/normalize-arguments.js:262:19)
at get (/home/runner/node_modules/got/dist/source/request-as-event-emitter.js:39:55)
at /home/runner/node_modules/got/dist/source/request-as-event-emitter.js:244:19
(node:331) UnhandledPromiseRejectionWarning: TypeError: The GET method cannot be used with a body
at Object.exports.normalizeRequestArguments (/home/runner/node_modules/got/dist/source/normalize-arguments.js:262:19)
at get (/home/runner/node_modules/got/dist/source/request-as-event-emitter.js:39:55)
at /home/runner/node_modules/got/dist/source/request-as-event-emitter.js:244:19
(node:331) UnhandledPromiseRejectionWarning: Unhandled promiserejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:331) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code."

@johanhermansson
Copy link
Author

@pedromgapt I would guess the URLs at Taste has changed :-/

The script probably needs adjustments for this

@pedromgapt
Copy link

well that was a fast reply :P
yeah, i noticed. i tried to find out what the url was ( i assumed the search ) but i failed :p
the bad part of this script ( that its still useful! ) is that it relies on movie names and not the imdb id, that´s why it will rate random movies we didnt watch sometimes. ( takes time, but easily fixed if we check the log )
it´s amazing how taste still hasnt deal with this, almost all sites have sync, import or even a very small google chrome extension that will help with this ( for example, a way to go to the movie page on taste, when we are on imdb ) ...

think you can find out the new url ?

@johanhermansson
Copy link
Author

johanhermansson commented Dec 7, 2019

@pedromgapt They probably don't like sync because we (I at least) are less motivated to set other attributes on movies.

Yes, the search functionality is not optimal. With more time the script could probably be better at this. For example it could also look at next results and "fuzzy" compare the titles together with release year (+- 1 year) 🙈

It would be nice to update the script. But there's a lot going on right now. Ping me over the holidays and maybe I feel more motivated 😉

@pedromgapt
Copy link

yeah i can understand that =) its all good.
btw, it would be easier / i think / to use trakt to import because you can create ur own app inside your user account.

@johanhermansson
Copy link
Author

@pedromgapt Full rewrite of the script could be found here https://github.com/johanhermansson/letterboxd-exporter

It's now separated into two separated scripts, one for exporting from Letterboxd, and another to import into Taste.io

@pedromgapt
Copy link

@johanhermansson thank you =) nice late Xmas present hehe :)
happy 2020, going to test it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment