IMDB "Where Have I Seen" tool

By Shreevatsa R Last update Sep 25, 2010 — Installed 1,204 times.

There are 10 previous versions of this script.

// ==UserScript==
// @name          IMDB "Where Have I Seen" tool
// @description   Shows you what roles actors have had in movies you've seen
// @namespace     http://shreevatsa.wordpress.com/2008/08/09/where-have-i-seen/
// @version       1.5
// @require       http://www.cmi.ac.in/~shreevatsa/json2.js
// @require       http://ecmanaut.googlecode.com/svn/trunk/lib/gm/wget.js
// @require       http://usocheckup.redirectme.net/32442.js
// @include       http://imdb.com/title/*
// @include       http://*.imdb.com/title/*
// @include       http://imdb.com/name/*
// @include       http://*.imdb.com/name/*
// @include	  http://www.imdb.com/chart/*
// @include       http://www.imdb.com/*
// @include       http://*.imdb.com/*
// ==/UserScript==
//

// Copyright (c) 2008, Shreevatsa R.
// Released under the GNU GPL v2 or later: http://www.gnu.org/copyleft/gpl.html

/*

  The September 2010 redesign of IMDb broke this script. It may be a
  while before I fix this the right way. In the meantime, if you want
  to use this script, you can tell IMDb to use the old version: see
  http://www.imdb.com/help/show_leaf?redesignolddesign
  and then install this version.

  If you install this version, it will notify you when an update is available.

  --

  Requires Firefox 3.5 or later.

  1. Keep track of movies you've seen, by clicking on [+] next to them.
     These movies are highlighted wherever links to them occur on IMDb.

  2. When looking at a cast listing, click "..." next to an actor
     to see which other movies you've seen that actor in.  Click
     on "Where have I seen these actors?" to see this for all.

  If this doesn't work, use the Userscripts.org discussion at
  http://userscripts.org/scripts/discuss/32442, or leave a comment at
  http://shreevatsa.wordpress.com/2008/08/09/where-have-i-seen/

  Notes
  =====

  * This script keeps track of the seen movies in its own table (an
  about:config preference), not online on IMDb. It's a good idea to
  backup the list occasionally: Click on "Show seen movies" in "User
  script commands", hit Ctrl-A, Ctrl-C, Enter/Return, and copy text.
  If you know how to make it save to IMDB's "My movies" or somewhere
  online, any code contributed will be appreciated.

  * For each cast listing, it actually visits each actor's page to
  find all his/her movies. Not only can this be slow if you're on a
  slow connection, it might also be considered too much strain on
  IMDb's servers. Use with discretion.

  Changelog:

  2010-09-24 v1.5    Temporary fix for IMDb overhaul, add usocheck auto-update

  2009-12-30 v1.4    Update description, newlines in whis, improve remove-after-add

  2009-12-21 v1.3    Switch to "own" JSON @require library, mention Firefox 3.5

  2009-10-13 v1.2    Send one request per second, allow fetching just one actor

  2009-08-24 v1.1.1  Change @include to work on all of IMDb

  2009-08-24 v1.1    Show only one line per movie, not all the 'aka's.

  2009-08-02 v1.0    Make it fetch information only when asked.

  2008-08-28 v0.6    Script didn't work for new users; fixed now

  2008-08-26 v0.2    Removed cruft, switched to external "wget" library

  2008-08-25 v0.1    First upload

*/

"use strict";
/*jslint browser: true, onevar: false, white:false, plusplus: false */
/*global window, document, wget, GM_setValue, GM_getValue, GM_registerMenuCommand, GM_xmlhttpRequest, GM_log */

if(window===window.top) {
  (function () {
    //JSLint thinks function names starting with uppercase are constructors
    var gm_log=GM_log, gm_setValue=GM_setValue, gm_getValue=GM_getValue;
    var gm_registerMenuCommand=GM_registerMenuCommand, gm_xmlhttpRequest=GM_xmlhttpRequest;
    function assert(cond, str) { if (!cond) { throw new Error('Assertion failed: ' + str); } }
    function do_doc(url, func) { wget(url, func, /*runGM=*/false, /*div=*/true); }

    var things_to_do = [];
    function pop_queue(func) {
      if(things_to_do.length>0) { things_to_do.shift()(); }
      window.setTimeout(pop_queue, 1000);
    }
    pop_queue(); //Get it going

    var my_movies = gm_getValue('my_movies');
    if(my_movies === undefined) { my_movies='{}'; }
    my_movies =  JSON.parse(my_movies);


    function seen_movie(tt) { return my_movies[tt]!==undefined; }

    //get_tt("http://www.imdb.com/title/tt0111161/fullcredits#cast") = "0111161"
    function get_tt(s) {
      var matches = s.match(/tt(\d+)\/$/) || s.match(/tt(\d+)\/fullcredits/);
      if(matches && matches.length > 1) { return matches[1]; }
    }

    //Return a div with a list of seen films, given an actor's filmography
    function seen_filmography_div(filmo, excepttt) {
      var ret = document.createElement('div');
      var ul = document.createElement('ul');
      var movies = filmo.getElementsByClassName('label');
      for(var i=0; i<movies.length; ++i) {
        //First check if something's seen here
        var as = movies[i].getElementsByTagName('a');
        var someseen = false;
        for(var j=0; j<as.length; ++j) {
          var tt=get_tt(as[j].pathname);
          if(tt && tt!==excepttt && seen_movie(tt)) { someseen = true; }
        }
        //Now to add it...
        if(someseen) {
          //movies[i].innerHTML = movies[i].innerHTML.replace(/<br>.*/,'');
          var moviename = movies[i].getElementsByClassName('title')[0].innerHTML;
          var character = movies[i].getElementsByClassName('detail')[0].innerHTML;
          var listitem = document.createElement('div');
          listitem.innerHTML = moviename + character;
          ul.appendChild(listitem);
        }
      }
      ret.appendChild(document.createElement('p'));
      ret.appendChild(ul);
      ret.appendChild(document.createElement('p'));
      return ret;
    }

    //Given the actor's filmography page, return a div with a list of seen films
    function seen_filmography(doc, excepttt) {
      var type = '', filmo; //filmo = the list named 'actor' or 'actress'
      var filmos = doc.getElementsByClassName('posters'); //Contains all movies
      if(filmos.length) {
        assert(filmos.length==1, 'only one filmography');
        filmo = filmos[0];
        return seen_filmography_div(filmo, excepttt);
      }
    }

    //[When asked] For a cast row, get actor name, and add filmography accordingly
    function fiddle_castrow(crow, tt, linknode, name) {
     //What does this have to do with Cuba, anyway?
      if(!(crow.getElementsByClassName && crow.getElementsByClassName('nm').length)) { return; }
      var a = crow.getElementsByClassName('nm')[0].getElementsByTagName('a')[0];
      var page = 'http://m.imdb.com/' + a.pathname.substr(0,15) + '/filmotype/'; //The page for that actor
      things_to_do.push(function() {
        pages = [page+'actor', page+'actress'];
        for(var i=0; i<pages.length; ++i) { //Eeks
          do_doc(pages[i],
                 function(doc) {
                   var sfo = seen_filmography(doc, tt);
                   if(sfo) {
                     crow.appendChild(sfo);
                     linknode.innerHTML = linknode.innerHTML.replace('['+name+']', '');
                     if(linknode.innerHTML === '<small>Getting...</small>') { linknode.innerHTML = '<small>Done</small>'; }
                   }
                 });
        }
      });
    }

    //[When asked] Take a page with a "cast" in it, and work on each cast row.
    function fiddle_castpage(tt, linknode) {
      var castrows = document.getElementsByClassName('cast')[0].getElementsByTagName('tbody')[0].children || [];
      for(var i=0; i<castrows.length; ++i) {
        var crow = castrows[i];
        var name = '';
        try { name = crow.childNodes[1].childNodes[0].innerHTML; } catch(err) { continue; }
        linknode.innerHTML = linknode.innerHTML.replace('</small>', '['+name+']' + '</small>');
        fiddle_castrow(crow, tt, linknode, name);
      }
    }

    //Add a link that will get the seen_filmography for everyone in the cast
    function add_getwhis_link() {
      var casts = document.getElementsByClassName('cast');
      if(casts.length===0) { return; }
      assert(casts.length===1, 'Just one node with class "cast"');
      var tt = get_tt(document.location.pathname);
      if(!tt) { return; }

      var getwhis = document.createElement('a');
      getwhis.href = 'see://where-you-have-seen-each-actor-in-this-cast';
      getwhis.innerHTML = '<small>Where have I seen these actors?</small>';
      var onclick = function(event) {
        getwhis.innerHTML = '<small>Getting...</small>'; event.stopPropagation(); event.preventDefault();
        fiddle_castpage(tt, getwhis);
      };
      getwhis.addEventListener('click', onclick, false);
      assert(casts[0].tagName === "TABLE");
      for(var where=casts[0]; where !== document; where=where.parentNode) {
        if(where.previousElementSibling === null) { continue; }
        if(where.previousElementSibling.children[0].innerHTML.match("Cast")) {
          where.previousElementSibling.appendChild(getwhis);
          break;
        }
      }
      //Make all the "..." into links
      var castrows = casts[0].getElementsByTagName('tbody')[0].children || [];
      for(var i=0; i<castrows.length; ++i) {
        var crow = castrows[i], name, ddd, newddd;
        try { name = crow.childNodes[1].childNodes[0].innerHTML; } catch(err) { continue; }
        try { ddd = crow.getElementsByClassName('ddd')[0].childNodes[0]; } catch(errr) { continue; }
        newddd = document.createElement('a');
        newddd.innerHTML = " ... ";
        newddd.href = "show://just-this-actor";
        newddd.addEventListener('click', (function() {
              var ccrow = crow, ctt = tt, clink=getwhis, cname=name;
              return function(event) {
                event.stopPropagation(); event.preventDefault();
                fiddle_castrow(ccrow, ctt, clink, cname);
              };
            }()), false);
        ddd.parentNode.replaceChild(newddd, ddd);
      }
    }

    function highlight(a) {
      a.style.fontWeight = 'bold';
      a.style.color = 'green';
    }
    function unhighlight(a) {
      a.style.fontWeight = '';
      a.style.color = '';
    }

    //Add a link that says "[+]" and will add the movie to the list
    function add_addmovie_link(a, tt) {
      var link = document.createElement('a');
      link.href = 'add://this-movie-to-your-seen-list';
      link.innerHTML = '[+]';
      var onclick = function(event) {
        link.innerHTML = ' [Adding...]';
        event.stopPropagation();
        event.preventDefault();
        var url = 'http://www.imdb.com/title/tt' + tt + '/';
        do_doc(url, function(doc) {
            //The best way seems to be to get it from the photo! (Other ways will include the year, and fail on episodes.)
            my_movies[tt] = doc.getElementsByClassName('photo')[0].childNodes[1].title;
            gm_setValue('my_movies', JSON.stringify(my_movies));
            //link.innerHTML = '[\u2713]';
            link.innerHTML = '';
            highlight(a);
            add_removemovie_link(a, tt);
          });
      };
      link.addEventListener('click', onclick, false);
      a.parentNode.insertBefore(link, a.nextSibling);
    }

    function add_removemovie_link(a, tt) {
      var link = document.createElement('a');
      link.href = 'remove://this-movie-from-your-seen-list';
      link.innerHTML = '[-]';
      var onclick = function(event) {
        link.innerHTML = ' [Removing...]';
        event.stopPropagation();
        event.preventDefault();
        my_movies[tt] = undefined;
        gm_setValue('my_movies', JSON.stringify(my_movies));
        //link.innerHTML = '[\u2713]'; //A tick mark
        link.innerHTML = '';
        unhighlight(a);
        add_addmovie_link(a, tt);
      };
      link.addEventListener('click', onclick, false);
      a.parentNode.insertBefore(link, a.nextSibling);
    }


    //Highlighting "My movies" and making it easy to add them
    function modify_links() {
      var as = document.getElementsByTagName('a');
      for(var i=0; i<as.length; ++i) {
        var a = as[i];
        var tt = get_tt(a.href); if(!(tt)) { continue; }
        if(seen_movie(tt)) { highlight(a); add_removemovie_link(a,tt); }
        else { add_addmovie_link(a, tt); }
      }
    }

    function modify_title() {
      var tt = get_tt(document.location.href);
      if(!tt) { return; }
      if(!seen_movie(tt)) { return; }
      var title = document.getElementById('tn15title');
      if(title) { highlight(title); }
    }


    // ---------- "main" ----------

    modify_links();
    modify_title();
    add_getwhis_link();

    gm_registerMenuCommand('Show seen movies', function() { alert(JSON.stringify(my_movies, null, 1)); }, 'S', 'shift control');

  }());
 }



// -*-
// js-indent-level: 2