Skip to content

Instantly share code, notes, and snippets.

@johanhermansson
Created August 7, 2019 07:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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

just what i was looking for. does this still work? if so, what happens if the movie is missing from taste database? thanks in advance.

@johanhermansson
Copy link
Author

@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.

@pedromga
Copy link

@johanhermansson , i gotta learn how to run this script 1st. im not a coder, im just someone that loves to use taste but has 4200 movies seen on imdb and i cant just put then there 1 by 1 :(

@johanhermansson
Copy link
Author

@pedromga I understand. Do you only have your ratings on Imdb? Not on Letterboxd? This script is for importing scripts from Letterboxd to Taste. Do you have PC or Mac?

@pedromga
Copy link

@johanhermansson ,i have both imdb and Letterboxd, and i have a PC.

@johanhermansson
Copy link
Author

johanhermansson commented Sep 30, 2019

@pedromga You need to start download Node from here: https://nodejs.org/en/download/ If you have a x64 OS you need to download the 64-bit version. Install it. If it asks if you want to add Node to your PATH, do it :)

Then you can create a directory somewhere, maybe on your desktop.

Create a new file in that directory, with notepad: letterboxd2taste.js, and copy the code above into the file.

Open a command line (in start menu, type cmd and it should be selected)

Try node -v and enter to see if you have node installed, it should output a version

Then cd ~/Desktop/yourfoldername

dir should list the file. If it's named letterboxd2taste.js.txt you need to rename it with ren letterboxd2taste.js.txt letterboxd2taste.js

(Desktop could be translated into your language)

Then you need to follow the instructions above. Remember to copy the auth token from the /me endpoint from Taste.io. Go to the webpage in Chrome, login and inspect the webpage (F12 or right click). Refresh the page with the Inspector open. In the "Network" tab you will find a /me url, selecting that should have a Response tab. In the Response tab you should find a token.

The auth token has a life time so if you wait a day you need to update it in the code.

I'm not working on a PC so I can't help if something goes wrong

@pedromga
Copy link

@johanhermansson oh uau, thank you so much. i was trying to get some info from google in how to run scripts on our pc, but this defenitly helps. i was kind of scared to ask for help on this website :P thank you very much, i will try it once i get home! ( the few stuff i dll from this website are normally .exe files with a nice UI that does the job =P )

@pedromga
Copy link

pedromga commented Oct 2, 2019

username: "PedroAbreu"
uuid: "62b922f0-e3ca-11e9-a6ab-dd432e2fb914"

is this the token? @johanhermansson ?

im using https://repl.it

@pedromga
Copy link

pedromga commented Oct 2, 2019

"Rating Camp Wedding as 3 -> 1...
{ HTTPError: Response code 401 (Unauthorized)
at EventEmitter.emitter.on (/home/runner/node_modules/got/source/as-promise.js:74:19)
at process._tickCallback (internal/process/next_tick.js:68:7)"

@johanhermansson
Copy link
Author

@pedromga No, they seems to have changed the token structure. You need to pick it from the request header:

Look here:
https://www.dropbox.com/s/bx969fhskhjfkd2/Screenshot%202019-10-02%2011.37.34.png?dl=0

(I have censored my token. You should select what I have marked with red :)

@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