FILMFLAM(1)                                                                    FilmFlam                                                                    FILMFLAM(1)

NAME
       filmflam - FilmFlam 2026.6.25

INTRODUCTION
       FilmFlam (or just "flam") is a commandline tool and API for extracting insights from your movie lists.

       From the commandline, flam enables you to quickly answer questions like "where have I seen this actor?", or "which director have I seen the most movies from?",
       and so much more.

       For more powerful uses, you can import flam and use the underlying API to access all the data about the movies and people in your movie lists.  [image] [image]

   Features
       o Get information about all the movies in your movie list, or about the people in those movies

       o Filter those movies or people to find something specific

       o Sort everything and everyone and discover standouts

       o Extend flam to support custom movie databases, custom filters, and custom attributes

       o All in a short and quick commandline

       o All in a python API for using from a script

   Installation
       Simply:

          pip install the-film-flam

       Then test that it worked with:

          flam --help

       If  it worked, great! Otherwise, python probably didn't add the folder where scripts get installed to PATH.  Find where flam.exe is installed on your machine -
       On Windows it will probably be in a folder that looks similar to one of these:

          %LOCALAPPDATA%\Python\pythoncore-3.14-64\Scripts
          %LOCALAPPDATA%\Programs\Python\Python312\Scripts

       Once you find it, add the folder to PATH and try again.

   Basic usage
       WARNING:
          This tutorial assumes your list is on IMDb and is public.  If that is not the case, read the full list of supported fetchers and pick the one  that's  right
          for you.

       The  first thing you need to do is let flam know which movie list you want to use. For IMDb lists, it's easy to find the list ID from the URL on the list page:
       [image]

       So in this case the list ID is 083886771. Let's configure it:

          flam config list watched imdb-listid=083886771

       Now flam will know this list by the name "watched", and it knows that this list can be downloaded using the "fetcher" imdb-listid.

       TIP:
          Fetchers tell flam how to download a list - different fetchers may download from different sites (IMDb, Letterboxd, etc.), or in different ways  (the  offi-
          cial IMDb API, or some unofficial one, etc.).

       Next, assuming your list is configured, we can fetch all its data. Run:

          flam fetch watched

       This  will automatically open your browser and click some buttons to export the IMDb list to CSV, so don't be alarmed by that.  Fetching can be quick or it may
       take several hours, depending on the size of your list. It's safe to interrupt this in the middle. Once it's done, you're ready to start using flam!

          # Browse all movies in the list.
          flam find movies watched

          # Find directors named "tarantino" including all their movies from the list.
          flam find director watched -name tarantino

          # Find people from any movie in the list whose average rating across those movies is at least 8.
          flam find people watched -avg-rating +8

          # Find the tallest actors in the list.
          flam find --sort height cast-people watched

          # Find movies both written and directed by women.
          flam find movies watched -every-role [ writer director ] -gender female

          # Find people who share your birthday :)
          flam find people watched -birth-month-day 07-25

   What's next
       o Configure your lists with --default-fetch=yes, --default-find=yes so you don't need to type their name every time

       o Re-run flam fetch whenever you want to refresh the database after adding movies to your list

       o Flam supports finding movies, people, or roles. Read all about what they mean

       o Read about filters for composing more complex queries. Any random question you can think of, flam can answer!*

       o Read more about how to configure lists and configure flam with all your movie lists!

       o Read the API docs if you're interested in using flam from a python script

       * Probably :)

   Supported platforms
       Flam is cross-platform and should work on Windows, Linux, and macOS. However, it's only well-tested on Windows, since that is what I am using.  Please  let  me
       know if you encounter issues.

       Flam is better if you have less installed. If you're on Windows, it's recommended to install it. One easy way is to just install git.

   Getting help
       If you encounter issues or have questions, you can open a GitHub issue, or email me at ederyaviv2@gmail.com.

   Disclaimer
       This program uses TMDB and the TMDB APIs but is not endorsed, certified, or otherwise approved by TMDB.

       Likewise IMDb, Letterboxd, or what-have-you.  [image]

FINDABLES
       It's good to understand the "findable" objects in flam - objects that can be found in a movie list. There are three findable types: movies, people, or roles.

       Findable  objects  have  many attributes. For example, `title' is a movie attribute, and `birthday' is a people's attribute.  Click here to read more about at-
       tributes and see a list of all builtin attributes.

   Movies
       Movies are exactly what you think - movies. You can browse all the movies in your list:

          flam find movies

   People
       In the simplest case, you can find all the people in any of the movies in the list.  But people can also be confined to a specific crew  type  (e.g.  director,
       cast, etc.), and they can also be grouped by collaborators.

       Formally, when finding people, each object is a list of people (could be more than 1) + a crew type + a group mode.

   Crew type
       Flam supports the following crew types:

              director
              assistant-director
              producer
              executive-producer
              composer
              cinematographer
              choreographer
              editor
              writer
              cast
              stuntcast
              casting-director
              art-director
              additional
              any

       The  same  person  can have different properties when presented as different crew types.  For example, Quentin Tarantino will have a different `num-movies' at-
       tribute as a director than as a cast member.

       The `any' crew type is special - it means you don't care about the crew type, you just want to find the people who were in the movies regardless of  what  they
       did in those movies.

   Group mode
       Grouping lets you turn collaborators into a single entity - when you search for directors, you probably want to see the Coen brothers as a single director.

       People objects are always attached to a group mode: `separate' or `group'.

       o When separate, each returned object represents a single person.

       o When grouped, flam will find people who've collaborated on movies together and return them as a single object.

       Every crew type has a default group mode that makes sense for it. Directors are grouped by default, actors are separate. This means that if you:

          flam find director-people

       The Coen brothers will be shown as a single entry.

   Roles
       A role is an appearance of some people in some movie. Think "Cristoph Waltz as a castmember in Inglorious Basterds".  So when searching for roles, you will see
       an entry per people per movie.

       Roles also have a crew type and a group mode like people do.

       Since they combine a people and a movie, roles support any movie attribute, people attribute, and also their own role attributes.

          # Browse directors and also the movies they directed from the list.
          flam find director

ATTRIBUTES
       Attributes  are  all  the  information  bits  you  can know about a findable object.  Things like the title of a film, its metascore, the name of a person, his
       height, etc. are all attributes.

       Attributes provide some facilities for using them generically without caring about the specific attribute:

       o They let you sort the attribute's values

       o They let you convert values to strings

       o They let you parse those strings back into values

       o Flam generically handles attributes which return lists

       o Flam generically handles attributes whose values are missing (i.e. not available in the fetch source)

       o There are a few more ways that attributes support generic handling

       Each attribute is specific to some findable type, but role attributes support any attribute of movies or people!

       TIP:
          Flam supports implementing custom attributes.

   List of builtin attributes
       Note that almost every attribute may return None if the data is not available. As a string, it looks like '-'.

       Many attributes below support variants. Here are the variants we support:

   len-X
       For some attribute X, "len-X" is the length of the attribute as a string.

       Ex: len-title - the length of the movie's title.

   num-X
       For some attribute X, "num-X" is the number of elements in the attribute if it returns a list. For non-lists, the value is 1.

       Ex: num-cast - the number of cast members in a movie.

   avg-X
       For some movie attribute X, "avg-X" is a people attribute which returns the average of X across all movies those people were in.

       Ex: avg-rating - the average rating across all movies the person was in.

       For a people attribute X, "avg-X" is a movie attribute which returns the average of X for all the people in the movie.

       Ex: avg-height - the average height of everyone in the movie.

   avg-X-as-CT
       Any attribute which supports avg-X also supports "avg-X-as-CT", where CT is a crew type.

       For some movie attribute X, "avg-X-as-CT" is a people attribute which returns the average of X across all movies those people were in as the crew type CT.

       Ex: avg-rating-as-director - the average rating across all movies the person directed.

       For a people attribute X, "avg-X-as-CT" is a movie attribute which returns the average of X for all the people in the movie as the crew type CT.

       Ex: avg-height-as-cast - the average height of all actors in the movie.

   sum-X
       Same as avg-X, but with summation instead of averaging.

   sum-X-as-CT
       Same as avg-X-as-CT, but with summation instead of averaging.

   Movie attributes
       additional - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       art-director - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       assistant-director - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       budget-usd - the movie's budget in US dollars.

          variants
                 len, num, avg, sum

          aliases
                 budget

          ascending
                 False

          default op
                 `='

       cast - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       casting-director - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       choreographer - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       cinematographer - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       composer - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       content-rating - the movie's content rating.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       countries - list of the movie's producing countries.

          variants
                 len, num

          aliases
                 country, nation, nations, nationality, nationalities

          ascending
                 True

          default op
                 `~'

       director - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       editor - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       end-date - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-d

          ascending
                 False

          default op
                 `='

       end-day-of-month - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-d-o-m

          ascending
                 True

          default op
                 `='

       end-day-of-week - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-d-o-w

          ascending
                 True

          default op
                 `='

       end-day-of-week-monday - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-d-o-w-m

          ascending
                 True

          default op
                 `='

       end-day-of-year - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-d-o-y

          ascending
                 True

          default op
                 `='

       end-month - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-m

          ascending
                 True

          default op
                 `='

       end-month-day - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-m-d

          ascending
                 True

          default op
                 `='

       end-week-of-year - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-w-o-y

          ascending
                 True

          default op
                 `='

       end-week-of-year-monday - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-w-o-y-m

          ascending
                 True

          default op
                 `='

       end-year - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-y

          ascending
                 False

          default op
                 `='

       end-year-month - the TV show's final air date in this date format.

          variants
                 len, num

          aliases
                 e-y-m

          ascending
                 False

          default op
                 `='

       episodes-num - the number of episodes in this TV show.

          variants
                 len, num, avg, sum

          aliases
                 episodes

          ascending
                 False

          default op
                 `='

       executive-producer - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       genres - list of the movie's genres.

          variants
                 len, num

          aliases
                 genre, category, categories

          ascending
                 True

          default op
                 `~'

       is-liked - whether you gave the film a like.

          variants
                 len, num

          aliases
                 liked, my-like

          ascending
                 False

          default op
                 `='

       languages - list of the movie's languages.

          variants
                 len, num

          aliases
                 language

          ascending
                 True

          default op
                 `~'

       likes - how many people liked the film.

          variants
                 len, num, avg, sum

          aliases
                 like-count

          ascending
                 False

          default op
                 `='

       list-index - list of the movie's indexes in the lists it came from.

          variants
                 len, num

          aliases
                 index, list-indices, indices, list-indexes, indexes

          ascending
                 True

          default op
                 `='

       list-note - list of notes left on this movie in this list.

          variants
                 len, num

          aliases
                 list-comment, list-notes, list-comments

          ascending
                 True

          default op
                 `~'

       listing-date - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-d

          ascending
                 False

          default op
                 `='

       listing-day-of-month - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-d-o-m

          ascending
                 True

          default op
                 `='

       listing-day-of-week - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-d-o-w

          ascending
                 True

          default op
                 `='

       listing-day-of-week-monday - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-d-o-w-m

          ascending
                 True

          default op
                 `='

       listing-day-of-year - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-d-o-y

          ascending
                 True

          default op
                 `='

       listing-month - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-m

          ascending
                 True

          default op
                 `='

       listing-month-day - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-m-d

          ascending
                 True

          default op
                 `='

       listing-week-of-year - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-w-o-y

          ascending
                 True

          default op
                 `='

       listing-week-of-year-monday - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-w-o-y-m

          ascending
                 True

          default op
                 `='

       listing-year - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-y

          ascending
                 False

          default op
                 `='

       listing-year-month - list of when you added this movie to this list or all lists compositing this list, in this date format.

          variants
                 len, num

          aliases
                 l-y-m

          ascending
                 False

          default op
                 `='

       media-type - whether this "movie" is actually a movie, or a TV series, etc.

          variants
                 len, num

          aliases
                 type, title-type

          ascending
                 True

          default op
                 `~'

       metascore - the movie's metascore.

          variants
                 len, num, avg, sum

          aliases
                 meta-score, metacritic-score

          ascending
                 False

          default op
                 `='

       metascore-votes - how many critic's reviews went into this movie's metascore.

          variants
                 len, num, avg, sum

          aliases
                 metacritic-review-count, metacritic-vote-count, metascore-review-count, metascore-vote-count, metacritic-rating

          ascending
                 False

          default op
                 `='

       my-notes - list of notes left on this movie by you in general.

          variants
                 len, num

          aliases
                 my-note, my-comment, my-comments

          ascending
                 True

          default op
                 `~'

       my-rating - the rating you gave to this movie.

          variants
                 len, num, avg, sum

          aliases
                 my-score, my-ratings, myrating

          ascending
                 False

          default op
                 `='

       origin-uid - the movie's UID in the source from which the data is fetched.

          variants
                 len, num

          aliases
                 origin-id, origin-guid, origin-identifier

          ascending
                 True

          default op
                 `~'

       original-title - the movie's original title in its native language.

          variants
                 len, num

          aliases
                 original-name, native-title, native-name

          ascending
                 True

          default op
                 `~'

       oscar-noms - list of Oscar nominations received by this movie.

          variants
                 len, num

          aliases
                 oscar-nominations, noms, nominations

          ascending
                 True

          default op
                 `~'

       oscar-wins - list of Oscars won by this movie.

          variants
                 len, num

          aliases
                 oscars, awards

          ascending
                 True

          default op
                 `~'

       people - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       producer - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       profit-usd - the movie's profit in US dollars.

          variants
                 len, num, avg, sum

          aliases
                 profit

          ascending
                 False

          default op
                 `='

       rating - the movie's rating.

          variants
                 len, num, avg, sum

          aliases
                 user-rating, user-score, ratings

          ascending
                 False

          default op
                 `='

       release-date - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-d

          ascending
                 False

          default op
                 `='

       release-day-of-month - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-d-o-m

          ascending
                 True

          default op
                 `='

       release-day-of-week - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-d-o-w

          ascending
                 True

          default op
                 `='

       release-day-of-week-monday - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-d-o-w-m

          ascending
                 True

          default op
                 `='

       release-day-of-year - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-d-o-y

          ascending
                 True

          default op
                 `='

       release-month - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-m

          ascending
                 True

          default op
                 `='

       release-month-day - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-m-d

          ascending
                 True

          default op
                 `='

       release-week-of-year - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-w-o-y

          ascending
                 True

          default op
                 `='

       release-week-of-year-monday - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-w-o-y-m

          ascending
                 True

          default op
                 `='

       release-year - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-y

          ascending
                 False

          default op
                 `='

       release-year-month - the movie's release date in this date format.

          variants
                 len, num

          aliases
                 r-y-m

          ascending
                 False

          default op
                 `='

       revenue-usd - the movie's revenue in US dollars.

          variants
                 len, num, avg, sum

          aliases
                 revenue

          ascending
                 False

          default op
                 `='

       runtime - the movie's runtime in minutes.

          variants
                 len, num, avg, sum

          aliases
                 runtime-minutes, length, minutes, run-length, run-time

          ascending
                 True

          default op
                 `='

       seasons-num - the number of seasons in this TV show.

          variants
                 len, num, avg, sum

          aliases
                 seasons

          ascending
                 False

          default op
                 `='

       source - for composite lists - which lists this movie came from.

          variants
                 len, num

          aliases
                 sources, origin, origins, src, list, lists

          ascending
                 True

          default op
                 `~'

       stars - list names of the movie's starring actors.

          variants
                 len, num

          aliases
                 star-cast, leads, lead-actors, lead-cast

          ascending
                 True

          default op
                 `~'

       studios - list of the movie's producing studios.

          variants
                 len, num

          aliases
                 studio

          ascending
                 True

          default op
                 `~'

       stuntcast - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       synopsis - the movie's synopsis.

          variants
                 len, num

          aliases
                 plot, summary, description, desc, overview

          ascending
                 True

          default op
                 `~'

       tagline - the movie's tagline.

          variants
                 len, num

          aliases
                 slogan

          ascending
                 True

          default op
                 `~'

       title - the movie's title.

          variants
                 len, num

          aliases
                 name, movie

          ascending
                 True

          default op
                 `~'

       uid - the movie's UID in flam.

          variants
                 len, num

          aliases
                 id, guid, identifier

          ascending
                 True

          default op
                 `~'

       url - link to the movie's page on the website it was fetched from.

          variants
                 len, num

          aliases
                 link, page

          ascending
                 True

          default op
                 `~'

       votes - how many people voted on this movie's rating.

          variants
                 len, num, avg, sum

          aliases
                 vote-count

          ascending
                 False

          default op
                 `='

       watch-date - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-d

          ascending
                 False

          default op
                 `='

       watch-day-of-month - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-d-o-m

          ascending
                 True

          default op
                 `='

       watch-day-of-week - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-d-o-w

          ascending
                 True

          default op
                 `='

       watch-day-of-week-monday - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-d-o-w-m

          ascending
                 True

          default op
                 `='

       watch-day-of-year - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-d-o-y

          ascending
                 True

          default op
                 `='

       watch-month - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-m

          ascending
                 True

          default op
                 `='

       watch-month-day - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-m-d

          ascending
                 True

          default op
                 `='

       watch-week-of-year - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-w-o-y

          ascending
                 True

          default op
                 `='

       watch-week-of-year-monday - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-w-o-y-m

          ascending
                 True

          default op
                 `='

       watch-year - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-y

          ascending
                 False

          default op
                 `='

       watch-year-month - list of dates that you watched the film in this date format.

          variants
                 len, num

          aliases
                 w-y-m

          ascending
                 False

          default op
                 `='

       writer - list of names of the movie's crewmembers in this crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

   People attributes
       birth-date - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 birthday, b-d

          ascending
                 False

          default op
                 `='

       birth-day-of-month - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-d-o-m

          ascending
                 True

          default op
                 `='

       birth-day-of-week - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-d-o-w

          ascending
                 True

          default op
                 `='

       birth-day-of-week-monday - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-d-o-w-m

          ascending
                 True

          default op
                 `='

       birth-day-of-year - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-d-o-y

          ascending
                 True

          default op
                 `='

       birth-month - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-m

          ascending
                 True

          default op
                 `='

       birth-month-day - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-m-d

          ascending
                 True

          default op
                 `='

       birth-week-of-year - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-w-o-y

          ascending
                 True

          default op
                 `='

       birth-week-of-year-monday - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-w-o-y-m

          ascending
                 True

          default op
                 `='

       birth-year - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-y

          ascending
                 False

          default op
                 `='

       birth-year-month - list of every person's birthday in this date format.

          variants
                 len, num

          aliases
                 b-y-m

          ascending
                 False

          default op
                 `='

       countries - list of every person's countries.

          variants
                 len, num

          aliases
                 country, nation, nations, nationality, nationalities

          ascending
                 True

          default op
                 `~'

       crew-type - the people's crew type.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       death-date - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-d

          ascending
                 False

          default op
                 `='

       death-day-of-month - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-d-o-m

          ascending
                 True

          default op
                 `='

       death-day-of-week - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-d-o-w

          ascending
                 True

          default op
                 `='

       death-day-of-week-monday - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-d-o-w-m

          ascending
                 True

          default op
                 `='

       death-day-of-year - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-d-o-y

          ascending
                 True

          default op
                 `='

       death-month - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-m

          ascending
                 True

          default op
                 `='

       death-month-day - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-m-d

          ascending
                 True

          default op
                 `='

       death-reason - list of every person's death reason.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       death-week-of-year - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-w-o-y

          ascending
                 True

          default op
                 `='

       death-week-of-year-monday - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-w-o-y-m

          ascending
                 True

          default op
                 `='

       death-year - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-y

          ascending
                 False

          default op
                 `='

       death-year-month - list of every person's death date in this date format.

          variants
                 len, num

          aliases
                 d-y-m

          ascending
                 False

          default op
                 `='

       gender - list of every person's gender (the exact strings representing each gender may vary based on where the data was fetched from).

          variants
                 len, num

          aliases
                 sex

          ascending
                 True

          default op
                 `~'

       group-mode - the people's group mode.

          variants
                 len, num

          aliases

          ascending
                 True

          default op
                 `~'

       height-cm - list of every person's height in centimeters.

          variants
                 len, num, avg, sum

          aliases
                 height

          ascending
                 False

          default op
                 `='

       movies - list of movie titles these people were in.

          variants
                 len, num

          aliases
                 titles, credits

          ascending
                 True

          default op
                 `~'

       movies-as-additional - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-additional, credits-as-additional

          ascending
                 True

          default op
                 `~'

       movies-as-any - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-any, credits-as-any

          ascending
                 True

          default op
                 `~'

       movies-as-art-director - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-art-director, credits-as-art-director

          ascending
                 True

          default op
                 `~'

       movies-as-assistant-director - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-assistant-director, credits-as-assistant-director

          ascending
                 True

          default op
                 `~'

       movies-as-cast - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-cast, credits-as-cast

          ascending
                 True

          default op
                 `~'

       movies-as-casting-director - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-casting-director, credits-as-casting-director

          ascending
                 True

          default op
                 `~'

       movies-as-choreographer - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-choreographer, credits-as-choreographer

          ascending
                 True

          default op
                 `~'

       movies-as-cinematographer - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-cinematographer, credits-as-cinematographer

          ascending
                 True

          default op
                 `~'

       movies-as-composer - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-composer, credits-as-composer

          ascending
                 True

          default op
                 `~'

       movies-as-director - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-director, credits-as-director

          ascending
                 True

          default op
                 `~'

       movies-as-editor - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-editor, credits-as-editor

          ascending
                 True

          default op
                 `~'

       movies-as-executive-producer - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-executive-producer, credits-as-executive-producer

          ascending
                 True

          default op
                 `~'

       movies-as-producer - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-producer, credits-as-producer

          ascending
                 True

          default op
                 `~'

       movies-as-stuntcast - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-stuntcast, credits-as-stuntcast

          ascending
                 True

          default op
                 `~'

       movies-as-writer - list of movie titles these people were in as this crew type.

          variants
                 len, num

          aliases
                 titles-as-writer, credits-as-writer

          ascending
                 True

          default op
                 `~'

       name - list of every person's name.

          variants
                 len, num

          aliases
                 names, person, people

          ascending
                 True

          default op
                 `~'

       origin-uid - list of every person's UID in the source from which the data is fetched.

          variants
                 len, num

          aliases
                 origin-id, origin-guid, origin-identifier

          ascending
                 True

          default op
                 `~'

       oscar-noms - list of Oscar nominations received by these people.

          variants
                 len, num

          aliases
                 oscar-nominations, noms, nominations

          ascending
                 True

          default op
                 `~'

       oscar-wins - list of Oscars won by these people.

          variants
                 len, num

          aliases
                 oscars, awards

          ascending
                 True

          default op
                 `~'

       professions - list of crew types occupied by these people.

          variants
                 len, num

          aliases
                 jobs, expertise

          ascending
                 True

          default op
                 `~'

       top-genres - list of up to 3 genres these people appear in the most.

          variants
                 len, num

          aliases
                 top-genre

          ascending
                 True

          default op
                 `~'

       uid - the people's UID in flam.

          variants
                 len, num

          aliases
                 id, guid, identifier

          ascending
                 True

          default op
                 `~'

   Role attributes
       In addition to the below, all movie and people attributes are also valid role attributes!

       characters - list of characters played by this role (mainly for actors).

          variants
                 len, num

          aliases
                 character

          ascending
                 True

          default op
                 `~'

       episodes-num - the number of episodes this role appeared in.

          variants
                 len, num

          aliases
                 episodes

          ascending
                 False

          default op
                 `='

       is-star - list of every person's star status in this movie - i.e. true or false based on if that person is starring.

          variants
                 len, num

          aliases
                 star

          ascending
                 False

          default op
                 `='

       jobs - list of jobs performed by this role (for crew type ADDITIONAL).

          variants
                 len, num

          aliases
                 job

          ascending
                 True

          default op
                 `~'

       oscar-noms - list of Oscar nominations received by this role.

          variants
                 len, num

          aliases
                 oscar-nominations, noms, nominations

          ascending
                 True

          default op
                 `~'

       oscar-wins - list of Oscar nominations received by this role.

          variants
                 len, num

          aliases
                 oscars, awards

          ascending
                 True

          default op
                 `~'

       uid - the role's UID in flam.

          variants
                 len, num

          aliases
                 id, guid, identifier

          ascending
                 True

          default op
                 `~'

FILTERS
       Flam lets you easily apply a filter when you search for findables. Filters can be very simple:

          # Filter directors to only ones named tarantino.
          flam find director -name tarantino

       Or more complex:

          # Find only movies released since 1980 and which contain at least one actor who was in either Lord of the Rings or Star Wars.
          flam find movies -release-year +1980 -any-role cast [ -movies lord.of.the.rings -o -movies star.wars ]

       Filters are essentially a combination of predicates. Each predicate checks one thing about the findable object.  By default predicates are joined together  us-
       ing AND (i.e., -pred1 -pred2 means "pred1 AND pred2"), but filters support all the standard features you'd expect:

       o AND: &, -a, -and
            This is the default

       o OR: |, -o, -or
            -pred1 -or -pred2

       o NOT: !, -n, -not
            -not -pred

       o Parentheses: [ ], ( ), -lparen -rparen
            Control the order of operations: [ -pred1 -pred2 ] -or [ -pred3 -pred4 ]

       TIP:
          Click here to read more about predicates and see a list of all builtin predicates.

   Formal syntax
       Below is the formal syntax for filters. Note that filters are case-sensitive!

          FILTER    := PIPELINE | <epsilon>
          PIPELINE  := SINGLE JOINABLE*
          SINGLE    := NEGATIVE | POSITIVE
          POSITIVE  := PREDICATE | [ PIPELINE ]
          NEGATIVE  := NOT POSITIVE
          JOINABLE  := CONJOINED | DISJOINED | SINGLE
          CONJOINED := AND SINGLE
          DISJOINED := OR SINGLE
          PREDICATE := -<name> <arg1> <arg2>...

          OR        := -o | -or  | `|`
          AND       := -a | -and | &
          NOT       := -n | -not | !
          [         := [  | (    | -lparen
          ]         := ]  | )    | -rparen

PREDICATES
       Predicates are the part of your filter that actually checks something about a findable:

          # -title is a predicate which checks if movie titles match some regular expression.
          flam find movies -title star.wars

          # -every-movie is a predicate which checks if every movie the people were in passes some subfilter.
          # -release-year is a subfilter, in this case we check if the movie was released not before the year 2000.
          flam find cast -every-movie -release-year +2000

       There are general predicates which are valid in any filter, and there's predicates specific to some findable type, which may only be used in filters for object
       of that type.

       TIP:
          Flam supports implementing custom predicates.

   Predicate arguments
       Most predicates have arguments. Below is a list of some common arguments predicates may have:

   ATTRIBUTE
       The name of some attribute:

          # Find only movies you've personally rated. my-rating is an ATTRIBUTE.
          flam find movies -has my-rating

   CMPTO
       CmpTo's express a comparison of some attribute's values to a constant, using a comparison operator of your choice.

       CmpTo's look like <operator><value>, as in "compare to <value> using <operator>". The <operator> is optional; every attribute has a default operator. For exam-
       ple:

          # '1980' is a CMPTO. It checks for equality by default.
          flam find movies -release-year 1980

          # Same as above but the operator is explicit. '=1980' is a CMPTO.
          flam find movies -release-year =1980

       Below are all supported operators:

          o - : Less or equal

          o + : Greater or equal

          o = : Exactly equal

          o .- : Strictly less than

          o .+ : Strictly greater than

          o ~ : Matches regular expression

   CTGM (crew type + group mode)
       CTGMs are a combination of a crew type and a group mode.

       CTGMs look like <crew type>:<group mode>. The <group mode> is optional; every crew type has a default group mode. For example:

          # Find movies that Brad Pitt acted in. 'cast' is a CTGM.
          flam find movies -any-role cast -name brad.pitt

          # Same as above. Castmembers are separate by default.
          flam find movies -any-role cast:separate -name brad.pitt

          # Find the writer duo of Peter Jackson and Frash Walsh. 'writer:group' is a CTGM.
          flam find movies -any-role writer:group [ -name peter.jackson -name fran.walsh ]

   SUBFILTER
       Some predicates accept an argument which is itself an entire filter. If the subfilter is made up of more than one predicate it must be parenthesized:

          # Find movies where every actor has an "e" in their name. '-name e' is a subfilter.
          flam find movies -every-role cast -name e

          # Find people who were in movies released between 1939 and 1945.
          # The subfilter is complex so it must be parenthesized.
          flam find people -any-movie [ -release-year +1939 -release-year -1945 ]

   LISTDEF
       Reference to some other movie list. See Listdefs.

   ... : List arguments
       Some predicates are documented like:

          -in-list LISTDEF...

       The ... means that it accepts a variable number of LISTDEF arguments. If you want to pass more than one, you'll have to parenthesize it:

          # Only one LISTDEF, no need to parenthesize.
          flam find movies -in-list cool-movies

          # Multiple LISTDEFs, must parenthesize.
          flam find movies -in-list [ cool-movies chill-movies ]

   List of builtin predicates
   Attribute predicates
       Every attribute in flam can also be used as a predicate to compare the attribute to a value. They look like -<attribute> CMPTO:

          # Check if the 'title' attribute matches a regular expression.
          flam find movies -title lord.of.the.rings

          # Check if the 'height' attribute is at least 180cm.
          flam find people -height +180

       NOTE:
          If an attribute returns a list, this will actually check if any element in the list matches this value.

   General predicates
       These predicates don't have a specific findable type. They may be used in any filter of any type.

       -true  Always true.

       -false Always false.

       -every ATTRIBUTE CMPTO
              For list attributes, check if every list element compares true.

       -has ATTRIBUTE
              True if we have some value populated for this attribute (i.e. not none or empty).

       -index ATTRIBUTE INDEX CMPTO
              True if the attribute compares true at the given index.

       -has-index ATTRIBUTE INDEX
              True if the attribute has a not-None value at this index.

       -superset ATTRIBUTE CMPTO...
              True if every list element in the attribute matches at least one of CMPTOs.

       -subset ATTRIBUTE CMPTO...
              True if every CMPTO matches at least one list element in the attribute.

       -sameset ATTRIBUTE CMPTO...
              True if every CMPTO matches at least one list element in the attribute and every list element in the attribute matches at least one CMPTO.

       -in-list LISTDEF...
              True if the findable is also in the list defined by LISTDEFs.

   Movie predicates
       -any-role CTGM... ROLES_SUBFILTER
              Scan the movie's crew according to CTGMs for a group which passes the filter ROLES_SUBFILTER.

       -every-role CTGM... ROLES_SUBFILTER
              Scan the movie's crew according to CTGMs to see if every group passes the filter ROLES_SUBFILTER.

   People predicates
       -any-movie MOVIES_SUBFILTER
              Scan the people's associated movies for one which passes the filter MOVIES_SUBFILTER.

       -every-movie MOVIES_SUBFILTER
              Scan the people's associated movies to see if they all pass the filter MOVIES_SUBFILTER.

       -as CREW_TYPE PEOPLE_SUBFILTER
              Check if the people pass the filter PEOPLE_SUBFILTER when they are another crew type.

       -any-person PEOPLE_SUBFILTER
              Separate the people if they are grouped and check if any of the split people pass the filter PEOPLE_SUBFILTER.

       -every-person PEOPLE_SUBFILTER
              Separate the people if they are grouped and check if every split person passes the filter PEOPLE_SUBFILTER.

   Role predicates
       There are currently no role-specific builtin predicates, but all movie and people predicates are also valid role predicates!

LISTS
       To work with flam is to work with movie lists. So it's good to familiarize yourself with how to reference lists, and what types of lists flam supports.

   Composite lists
       Flam  lets you define lists which are remixes of other lists you've configured.  After you've configured "simple" lists with flam config list, you may use them
       to define "composite" lists:

          # Define a list "rated" of only the movies from your "watched" list which you've personally rated.
          flam config composite rated watched -has my-rating

          # Define a list "all" which combines both your "watched-movies" and "watched-shows" lists.
          flam config composite all watched-movies watched-shows

       Generally, composite lists are one or more simple lists combined together, then optionally filtered.

   Listdefs
       "Listdefs" are how you describe which list you're talking about to flam. They have a number of forms:

       o "<fetcher>=<address>" (ex: "imdb-listid=083886771")
            A raw form for lists which haven't been given a name. The kind of address it expects depends on the fetcher.

       o "<name>" (ex: "watched")
            The name of a list configured with flam config list or flam config composite.

       o "<list type>=<name>" (ex: "list=watched")
            Like above, but with an explicit list type so there's no ambiguity. <list type> is either `list' or `composite'.

       o "*"
            A special listdef indicating to use all configured lists.

       o "defaults"
            A special listdef indicating to use all lists configured as --default-fetch (if you're using fetch), or --default-find (if you're using find).

FETCHERS
       Fetchers are how you download data about a movie list from some external source.

       When you configure a list, you will need to configure it with a listdef describing which fetcher to use, and the address of the list to download.

       Some fetchers allow you to control additional fetch settings using --fetch-param.

       TIP:
          Flam supports implementing custom fetchers.

   List of builtin fetchers
       imdb-listid=IMDB_LIST_ID
          Takes an IMDb list ID as an input, and downloads information in two steps:

          1. Export the list to CSV from the IMDb website - this will automatically launch your browser and click some buttons!

          2. Fill in a bunch of additional information using this free API: https://imdbapi.dev/

          It's easy to check what is your list's ID. Just open it in the browser, and the URL should look like this: https://www.imdb.com/list/ls083886771.  The  list
          ID in this example is "083886771".

          There are a few environment variables you can export to control the browser use:

          o FLAM_DOWNLOADS - Path to the downloads folder on your computer so flam will know where to look for the downloaded CSV.

                WARNING:
                   If your browser doesn't download things to ~/Downloads, you must set this variable for this fetcher to work!

          o FLAM_BROWSER - Which browser to use: `chrome', `edge', or `firefox'. By default flam tries to detect your default browser

          o FLAM_BROWSER_PROFILE  -  Path to your browser profile. This is only needed if your list is set to private so a profile is needed where you are expected to
            be already logged in.

          This fetcher also supports one --fetch-param:

          o csv-path - Skip exporting the list to CSV in the browser and instead use this path to an already downloaded file. Path may contain  environment  variables
            (%USERPROFILE%, $HOME, etc.).

                TIP:
                   Use this option as a workaround if you're having trouble with the automatic browser control feature. You will have to export the list manually.

       letterboxd-user-list=LETTERBOXD_USER_LIST
          Takes a Letterboxd user and list name in the form <username>/<list_slug> as an input, and downloads the list using letterboxdpy.

          List  slugs  are  just  the  list  name  as  it  appears  in  the  list  URL.  Just  open  it  in  the  browser,  and  the  URL  should  look  like  this: -
          https://letterboxd.com/verpous/list/movies-ive-watched/.  The <username>/<list_slug> in this example is "verpous/movies-ive-watched".

          A few special lists are also supported:

              o <username>/films - the user's watched films

              o <username>/likes - the user's liked films

              o <username>/reviews - the user's reviewed films

              o <username>/watchlist - the user's watchlist

          Note that Letterboxd provides us a lot of information about movies, but not so much about people. So this fetcher has a blindspot there.

       tmdb-list=TMDB_LIST_ID
          Takes a TMDB list ID as an input, and downloads the list using the TMDB API.

          It's   easy   to   check   what   is   your   list's   ID.   Just   open   it   in   the   browser,   and   the   URL   should    look    like    this:    -
          https://www.themoviedb.org/list/7103008-movies-i-ve-watched.  The list ID in this example is "7103008".

          A few special list IDs are also supported:

              o favorite-movies

              o favorite-shows

              o rated-movies

              o rated-shows

              o watchlist-movies

              o watchlist-shows

          In  order to use this fetcher, you MUST have a TMDB API token, and export it to the environment.  You can request an API token from your profile settings on
          TMDB.

          Once you've been granted access, take the "API Read Access Token" from your profile page and assign it to an environment variable named FLAM_TMDB_API_TOKEN:

              export FLAM_TMDB_API_TOKEN="abcdefgverylongstringofrandomcharacters"

COMMANDLINE
       FilmFlam comes packaged with a commandline tool you can run with the command flam:

          usage: flam [-h] [-F PATH] [-E] [-V] {config,fetch,find,docs} ...

          Gain insights on your movie lists. Quickly answer questions like "Where have I seen this actor?", or "Which director have I seen the most movies from?", and so much more.

          1. Create movie lists on IMDb, Letterboxd, or your website of choice
          2. Configure flam with how to download those lists with `flam config list`
          3. Download all the information about the movies in those lists with `flam fetch`
          4. Gain insights on the movies in those lists with `flam find`

          Subcommands:

              config      View or change the configuration - configure lists, custom extensions, etc.
              fetch       Download movie lists locally so they can be used
              find        Query for movies, people, or roles in your movie lists
              docs        Read the complete documentation

          The default subcommand is `find`. See `flam find --help` to know which arguments it accepts.

          I strongly recommend reading the docs! https://verpous.github.io/film-flam/introduction.html

          positional arguments:
            {config,fetch,find,docs}

          options:
            -h, --help            show this help message and exit
            -F PATH, --flam-dir PATH
                                  Use PATH as the flam directory - where flam stores all your data. Uses FLAM_DIR environment variable by default, or ~/.film_flam if it is not defined.
            -E, --no-extensions   Don't import configured extensions. Importing extensions executes arbitrary code so use this if you don't trust them.
            -V, --version         show program's version number and exit

          Examples:
              flam config list --default-fetch=yes --default-find=yes mylist imdb-listid=083886771
                  (Create a list 'mylist' with the IMDb list address)
              flam fetch
                  (Fetch all lists configured with --default-fetch)
              flam find movies
                  (View all movies in lists configured with --default-find)
              flam director mylist -name tarantino
                  (Uses find as the default subcommand. View information about directors in 'mylist' named 'tarantino' and all the movies they've directed from the list)

   flam config
          usage: flam config [-h] {list,composite,extension} ...

          View and modify the flam configuration. There are a few subcommands:

              list        Configure "simple" lists which are fetched from the web.
              composite   Configure "composite" lists which are remixes of your existing lists
              extension   Configure files to be imported containing your own custom attributes, predicates, and fetch sources

          positional arguments:
            {list,composite,extension}

          options:
            -h, --help            show this help message and exit

   flam config extension
          usage: flam config extension [-h] [-A | -D | -P] [IMPORT]

          View, add, or remove custom extension files. There are various things you can extend:

              Attributes      Values belonging to movies, people, or roles
              Predicates      Tests which can be used to filter movies, people, or roles
              Fetchers        Support for downloading movie lists from some specific website or API

          Read more about extensions: https://verpous.github.io/film-flam/extending.html.

          positional arguments:
            IMPORT        Specify which module or file to import. This can be a full path to a script or a module name if it is in PATH.

          options:
            -h, --help    show this help message and exit
            -A, --add     Add IMPORT as an extension. The default if IMPORT is provided.
            -D, --delete  Delete IMPORT from extensions.
            -P, --print   Print all extensions. The default if IMPORT is not provided.

          Examples:
              flam config extension ~/my_extension.py
                  (Register the extensions file ~/my_extension.py)
              flam config extension
                  (Print all extensions)
              flam config extension --delete ~/my_extension.py
                  (Delete the extension file ~/my_extension.py)

   flam config list
          usage: flam config list [-h] [-E | -D | -P] [-n NEW_NAME] [-i {yes,no,auto}]
                                  [-e {yes,no,auto}] [-p PARAM=VALUE] [-d PARAM]
                                  [NAME] [LISTDEF]

          View, add, edit, or remove lists.
          Once configured, lists can be easily used by their name in other commands like `flam fetch`, `flam find`.

          Read more about lists: https://verpous.github.io/film-flam/lists.html.

          positional arguments:
            NAME                  Operate on the list named NAME.
            LISTDEF               In --edit, set the list type and address to LISTDEF.

          options:
            -h, --help            show this help message and exit
            -E, --edit            Edit or create the list NAME. The default if NAME is provided.
            -D, --delete          Delete the list named NAME.
            -P, --print           Print the list NAME, or if NAME not provided, print all lists. The default if NAME is not provided.
            -n NEW_NAME, --rename NEW_NAME
                                  In --edit, rename the list to NEW_NAME.
            -i {yes,no,auto}, --default-find {yes,no,auto}
                                  In --edit, decide if the list should be default for `flam find`.
            -e {yes,no,auto}, --default-fetch {yes,no,auto}
                                  In --edit, decide if the list should be default for `flam fetch`.
            -p PARAM=VALUE, --fetch-param PARAM=VALUE
                                  In --edit, set a parameter for controlling how this list is fetched.
            -d PARAM, --delete-param PARAM
                                  In --edit, delete a parameter configured with --fetch-param ('*' to delete all).

          Examples:
              flam config list mylist imdb-listid=083886771
                  (Create a list 'mylist'. It's a local copy of the IMDb list '083886771' - https://www.imdb.com/list/ls083886771/)
              flam config list mylist --default-fetch=yes --default-find=yes
                  (Modify 'mylist' to be default for fetch and find, so `flam fetch`, `flam find` with no arguments will use this list)
              flam config list
                  (Print all lists)
              flam config list --rename ourlist mylist
                  (Rename 'mylist' to 'ourlist')
              flam config list --delete ourlist
                  (Delete 'ourlist')

   flam config composite
          usage: flam config composite [-h] [-E | -D | -P] [-n NEW_NAME]
                                       [-i {yes,no,auto}]
                                       [NAME] [SIMPLE_LIST ...] [MOVIE_FILTER ...]

          View, add, edit, or remove "composite" lists. These are remixes of your other lists which can combine multiple lists together and also filter them.
          Once configured, composite lists can be easily used by their name in other commands like `flam fetch`, `flam find`.

          Read more about composite lists: https://verpous.github.io/film-flam/lists.html.

          positional arguments:
            NAME                  Operate on the composite list named NAME.
            SIMPLE_LIST           In --edit, merge SIMPLE_LISTs to form this composite list.
            MOVIE_FILTER          In --edit, apply MOVIE_FILTER on the merged SIMPLE_LISTs to form this composite list.
                                  See the full documentation for filter syntax.

          options:
            -h, --help            show this help message and exit
            -E, --edit            Edit or create the composite list NAME. The default if NAME is provided.
            -D, --delete          Delete the composite list named NAME.
            -P, --print           Print the composite list NAME, or if NAME not provided, print all lists. The default if NAME is not provided.
            -n NEW_NAME, --rename NEW_NAME
                                  In --edit, rename the list to NEW_NAME.
            -i {yes,no,auto}, --default-find {yes,no,auto}
                                  In --edit, decide if the list should be default for `flam find`.

          Examples:
              flam config composite rated mylist -has my-rating
                  (Create a composite list 'rated'. It's got every movie in the simple list 'mylist' which has been rated by you)
              flam config composite owned dvds blurays
                  (Create a composite list 'owned'. It's got every movie from both simple lists 'dvds' and 'blurays')
              flam config composite
                  (Print all composite lists)
              flam config composite --delete rated
                  (Delete 'rated')

   flam fetch
          usage: flam fetch [-h] [-u] [-r PATTERN] [-p PARAM=VALUE] [LISTDEF ...]

          Download information about the movies in your movie lists. Lists must have been fetched at least once upon a time before you can use them.
          You should rerun this once in a while if you've made changes in your movie lists to sync them locally.

          positional arguments:
            LISTDEF               Which lists to fetch.
                                  In the case of a composite list, will actually fetch the simple lists which it's composited from.
                                  By default fetches all lists configured with --default-fetch.

                                  Read more about supported LISTDEFs: https://verpous.github.io/film-flam/lists.html.

          options:
            -h, --help            show this help message and exit
            -u, --undo            Undo the previous fetch operation in its entirety. Note this will also restore configuration to the old state.
                                  Fetch can be expensive so if something goes wrong and files get messed up this is good to have.
                                  You can rerun this to undo up to the last 3 fetches.
            -r PATTERN, --refetch PATTERN
                                  Forces titles that match PATTERN (case-insensitive regular expression) to be redownloaded even if they are already locally stored.
                                  It's enough for PATTERN to match any part of the title, not necessarily the whole title.
                                  This feature is intended for redownloading shows after a new season has come out.
            -p PARAM=VALUE, --fetch-param PARAM=VALUE
                                  Set a parameter for controlling how the lists are fetched.

          Examples:
              flam fetch
                  (Fetch all lists configured with --default-fetch)
              flam fetch dvds blurays
                  (Fetch the configured list "dvds" and "blurays")
              flam fetch --undo
                  (Undo the last fetch operation)
              flam fetch --refetch 'bojack' shows
                  (Refetch Bojack Horseman from the list "shows")

   flam find
          usage: flam find [-h] [-s ATTRIBUTES] [-c ATTRIBUTES] [-l ATTRIBUTES]
                           [-C {always,auto,never}] [-d DELIM] [-v] [-r] [-S]
                           [-P {always,auto,never}] [-t]
                           FINDABLE [LISTDEF ...] [FILTER ...]

          Explore "findables" (movies, people, or roles) in your movie lists, and query for specific ones which answer to some filter.
          Found objects are printed in a nice table format, and you can customize what is printed and how it's sorted.

          positional arguments:
            FINDABLE              Choose what to find: movies, people, or roles.

                                  People and roles support limiting the search to a specific crew type with an optional group mode, or comma-delimited several types. Use 'crew' to catenate all types.
                                  For roles, it looks like: 'cast', 'director:group', 'composer:separate,stuntcast', 'crew', etc.
                                  For people, it looks like 'cast-people', 'director-people:group', etc.

                                  Supported crew types: director, assistant-director, producer, executive-producer, composer, cinematographer, choreographer, editor, writer, cast, stuntcast, casting-director, art-director, additional, any

                                  Read more about findables: https://verpous.github.io/film-flam/findables.html

            LISTDEF               Which lists to search in. They must've been previously fetched. By default searches in all lists configured with --default-find.
                                  Read more about supported LISTDEFs: https://verpous.github.io/film-flam/lists.html.

            FILTER                Search only for findables which pass FILTER.
                                  Read more about filters: https://verpous.github.io/film-flam/filters.html.

          options:
            -h, --help            show this help message and exit
            -s ATTRIBUTES, --sort ATTRIBUTES
                                  Sort FINDABLEs according to ATTRIBUTES, which is a comma-delimited list of attributes to sort by, in decreasing priority.
                                  Each findable type has its own default:

                                      movies      release-year,title
                                      people      crew-type,group-mode,num-movies,name
                                      roles       crew-type,group-mode,num-movies,name,release-year,title

                                  For a full list of supported attributes: https://verpous.github.io/film-flam/attributes.html.
            -c ATTRIBUTES, --columns ATTRIBUTES
                                  Comma-delimited list of attributes of FINDABLE to print.
                                  If ATTRIBUTES starts with a '+' then they will be printed in addition to the defaults instead of instead.
            -l ATTRIBUTES, --split ATTRIBUTES
                                  Comma-delimited list of attributes. If their type is a list, each element will be split into a separate entry.
            -C {always,auto,never}, --color {always,auto,never}
                                  Set whether columns should be colored. Defaults to auto.
            -d DELIM, --dsv DELIM
                                  Output in delimiter-separated values format (DSV). I.e. if DELIM is ',' then that is CSV format.
            -v, --verbose         Use verbose output, where long strings are not truncated and some attributes may be printed in longer format.
            -r, --reverse         Reverse the sort order. By default some sort keys are ascending and some descending based on what makes sense. This reverses those defaults.
            -S, --spacious        Add an empty line between entries.
            -P {always,auto,never}, --paginate {always,auto,never}
                                  Choose whether to paginate with `less`. Defaults to auto, which depends on the size of the output.
            -t, --no-titles       Don't print a row with the column titles.

          Examples:
              flam find movies
                  (Find movies in the default lists)
              flam find cast shows -is-star true
                  (Find starring roles in the list 'shows')
              flam find --sort height director-people:separate,writer-people:separate -height +160 -height -180
                  (Find directors and writers whose height is between 160 and 180 centimeters and sort them by their height)
              flam find --columns +watch-date movies -metascore +70 -o -every-role [ writer director ] -gender female
                  (Find movies with a metascore above 70 or which were written and directed by women, and print their watch date alongside default columns)

   flam docs
          usage: flam docs [-h] [-b]

          View the documentation.

          options:
            -h, --help     show this help message and exit
            -b, --browser  Open the documentation in the browser.

API
       Flam is not just a commandline tool, it's also a python API for using from your scripts:

          import flam

          # Most interfacing with flam happens through a FlamContext. Create one then start using it.
          ctx = flam.FlamContext()

          # Configure a list 'watched' based on your IMDb list of movies you've watched.
          with ctx.configure() as cfg:
              simple_list = flam.SimpleList(
                  uid = "what you set here doesn't matter, it will be overwritten anyway",
                  name = 'watched',
                  concrete_listdef = ctx.parse_listdef('imdb-listid=083886771'),
                  is_default_fetch = False,
                  is_default_find = False,
              )

              cfg.simple_lists_raw.append(simple_list)

          # Download the list and store it persistently. Next time you won't have to fetch it again.
          ctx.fetch(['watched'])

          # Print the titles of watched movies from the year 1999.
          watched_movies = ctx.get_movie_list('watched')
          filter = ctx.compile_movies_filter(['-release-year', '1999'])

          for movie in watched_movies.find_movies(filter):
              print(movie['title'])

   flam
       This page documents the core API of flam. It includes everything you get when you import flam.

       DEFAULT_FLAM_DIR
              The default path where flam stores all your movie lists and configuration. Equal to the environment variable FLAM_DIR, or ~/.film_flam if it's  not  de-
              fined.

       class PrecachePreference
              Preference for what you'd like to be cached. For use with FlamContext.precache().

              DEFAULTS = 1
                     Generate all composite lists, and cache particularly expensive computations for all movie lists.

              EVERYTHING = 2
                     Cache everything that can possibly be cached.

              RESET = 3
                     Don't cache anything, instead delete existing cache files.

       class FlamContext
              Represents  a user of a flam directory. All API use generally begins with creating a context, and anything you might do with flam generally goes through
              the context.

              Only one context is allowed per flam directory at any given time.

              __init__(flam_dir: None | str = DEFAULT_FLAM_DIR, import_extensions: bool = False) -> None

                     Parameters

                            o flam_dir (None | str) -

                              the directory where all movie lists and configuration should be stored.

                              If this is None, flam will work in "volatile mode" - everything you fetch or configure will be lost when the context dies.

                            o import_extensions (bool) -

                              import all configured extensions, and subscribe to globally registered extensions.

                              WARNING:
                                 Only enable this if you trust the extensions in the configuration.

                     Return type
                            None

              property flam_dir: str
                     The directory where all movie lists and configuration are stored.

              property cfg_readonly: Configuration
                     All configuration settings. This object can technically be modified, but that would lead to disaster. If you want to  modify  the  configuration,
                     use configure().

              property fetchers: RegistriesOf[type[Fetcher]]
                     A registry object with all registered fetchers.

              property predicates: RegistriesOf[type[Predicate]]
                     A registry object with all registered predicates.

              property attributes: RegistriesOf[Attribute]
                     A registry object with all registered attributes.

              parse_listdef(listdef: str) -> CanonListdef
                     Parse a string representation of a listdef and canonicalize it.

                     Parameters
                            listdef (str) -

                            the listdef as a string. It can have a few forms:

                            o ALL

                            o DEFAULTS

                            o '<name>' - where <name> is the name of a simple or composite list

                            o '<list_type>=<address>' - where <list_type> is SIMPLE, COMPOSITE, or the name of a fetcher.

                     Return type
                            CanonListdef

              register(item: T) -> T
                     Register a context-level extension. Context extensions are only available from the specific context to which they were registered.

                     You may register an item with the same name as that of a global extension or a builtin, and it will shadow them.

                     Parameters
                            item (T) - the item to register.

                     Return type
                            T

              get_movie_list(listdefs: str | Iterable[str], filter: None | Filter = None) -> MovieList
                     Create or open a movie list.

                     Parameters

                            o listdefs  (str  | Iterable[str]) - indicates which lists to open, or which lists to composite into this movie list. They must all be al-
                              ready fetched.

                            o filter (None | Filter) - a movie filter used to filter out movies from the list. If provided, the returned  list  will  be  a  composite
                              list.

                     Return type
                            MovieList

              configure() -> Iterator[Configuration]
                     Returns the configuration settings inside a context where they may be modified. When the context exits, all changes are saved.

                        with ctx.configure() as cfg:
                            cfg.extensions.append('my_extension.py')

                     Return type
                            Iterator[Configuration]

              fetch(listdefs: Iterable[str], refetch_pattern: None | str = None, quiet: bool = True, **fetch_params: str) -> None
                     Fetch  movie  lists,  i.e.  download all their data from some external source and store it locally.  Depending on the fetcher and the size of the
                     list, this may run for a long time - even hours.

                     Parameters

                            o listdefs (Iterable[str]) -

                              which lists to fetch. For composite lists, will actually fetch all simple lists which composite it. A few special values are  also  sup-
                              ported:

                              o Supports `defaults' to fetch all lists configured with is_default_fetch=True

                              o Supports '*' to fetch all configured lists

                            o refetch_pattern  (None | str) - forces titles that match this regular expression (case-insensitive) to be refetched even if they are al-
                              ready locally stored.  The expression only needs to match any part of the title, not the whole title. Intended for  redownloading  shows
                              after a new season has come out.

                            o quiet (bool) - indicates if progress should be printed to stdout.

                            o fetch_params (str)

                     Return type
                            None

              precache(preference: PrecachePreference = PrecachePreference.DEFAULTS, quiet: bool = True) -> None
                     Precompute some things and cache them to disk so that flam will work faster. This is strictly optional.

                     Parameters

                            o preference (PrecachePreference) - what you prefer to be cached.

                            o quiet (bool) - if False, will print about its progress to stdout.

                     Return type
                            None

              compile_filter(tokens: list[str], find: FindableType) -> Filter
                     Compile a string into a filter object.

                     Parameters

                            o tokens (list[str]) -

                              the desired filter string split into tokens. Here is an example to illustrate how to split it:

                                 # As a string:
                                 '-title lebowski -o -release-year 1980'

                                 # Split into tokens:
                                 ['-title', 'lebowski', '-o', 'release-year', '1980']

                            o find (FindableType) - the type of objects the filter is supposed to filter. Some predicates may be specific to a certain findable type.

                     Return type
                            Filter

              compile_movies_filter(tokens: list[str]) -> Filter
                     Wrapper for compile_filter() when the filter is for MOVIES.

                     Parameters
                            tokens (list[str])

                     Return type
                            Filter

              compile_people_filter(tokens: list[str]) -> Filter
                     Wrapper for compile_filter() when the filter is for PEOPLE.

                     Parameters
                            tokens (list[str])

                     Return type
                            Filter

              compile_roles_filter(tokens: list[str]) -> Filter
                     Wrapper for compile_filter() when the filter is for ROLES.

                     Parameters
                            tokens (list[str])

                     Return type
                            Filter

       class RegistriesOf
              Object representing everything that is registered in a FlamContext of type T.  T may be either Attribute, type[Predicate], or type[Fetcher].

              __getitem__(qualified_name: str) -> T
                     Get a registered item.

                     Parameters
                            qualified_name (str) - the full, qualified name of the item. Qualified names include both the name and the type. E.g., instead of `title',
                            it should be `movies-title'.

                     Return type
                            T

              __contains__(qualified_name: str) -> bool
                     Check if the registry contains an item with this name.

                     Parameters
                            qualified_name (str) - the full, qualified name of the item.

                     Return type
                            bool

              __iter__() -> Iterator[T]
                     Iterate over all unique items in the registry.

                     To iterate over the raw registry, see raw_iterate().

                     Return type
                            Iterator[T]

              raw_iterate() -> Iterable[str]
                     Iterate over the names of all items in the registry. Beware:

                        o Items support aliases so the same item may be returned multiple times, once for each name it is known by

                        o Items  may  be  shadowed  if  a different item with the same name is registered in a higher-level registry, so this may return the same name
                          twice

                     To iterate over items without worrying about the above, use __iter__().

                     Return type
                            Iterable[str]

              get(name: str, type_hint: None | _ml.FindableType = None) -> T
                     Get a registered item, with optional support for non-qualified names.

                     Parameters

                            o name (str) - the name of the item. It must be fully qualified unless type_hint is given.

                            o type_hint (None | _ml.FindableType) -

                              attempt to infer the type from a non-qualified name, with a preference for this hinted type.  Multiple items can have the same name  but
                              with a different type, so this helps resolve that ambiguity.

                              WARNING:
                                 This does NOT guarantee that the returned item will have the same type! It only guarantees to prefer that type if there's ambiguity.

                     Return type
                            T

       class GroupMode
              Indicates  whether people who collaborate together should be grouped into a single "person".  For example, if grouping is enabled then the Coen brothers
              will be presented as a single entry when finding directors.

              DEFAULT = 'default'
                     Use whichever default makes sense for the crew type.

              GROUP = 'group'
                     Do try to group collaborators together.

              SEPARATE = 'separate'
                     Don't group - keep each person as a separate entry.

              classmethod iterate_except_default() -> Iterable[GroupMode]
                     Iterate over all values except DEFAULT.

                     Return type
                            Iterable[GroupMode]

       class CrewType
              A job on a movie set.

              DIRECTOR = 'director'

              ASSISTANT_DIRECTOR = 'assistant-director'

              PRODUCER = 'producer'

              EXECUTIVE_PRODUCER = 'executive-producer'

              COMPOSER = 'composer'

              CINEMATOGRAPHER = 'cinematographer'

              CHOREOGRAPHER = 'choreographer'

              EDITOR = 'editor'

              WRITER = 'writer'

              CAST = 'cast'

              STUNTCAST = 'stuntcast'

              CASTING_DIRECTOR = 'casting-director'

              ART_DIRECTOR = 'art-director'

              ADDITIONAL = 'additional'
                     Catch-all category for crew types not listed above.

              ANY = 'any'
                     No crew type in particular, only care about if the person was in the movie in any capacity.

              property default_group_mode: GroupMode
                     The group mode which makes the most sense for this crew type.

              classmethod iterate_except_any() -> Iterable[CrewType]
                     Iterate over all values except ANY.

                     Return type
                            Iterable[CrewType]

       parse_ct_gm(ct_gm_str: str) -> tuple[CrewType, GroupMode]
              Parse a CTGM, short for "crew type + group mode", and returns it as a tuple.

              CTGMs can be just the crew type or also the group mode with a colon delimiter. Ex: `director:group', `cast:separate', `writer:default' or just `writer'.

              Parameters
                     ct_gm_str (str) - the CTGM as a string.

              Return type
                     tuple[CrewType, GroupMode]

       ct_gm_to_str(crew_type: CrewType, group_mode: GroupMode) -> str
              Inverse of parse_ct_gm().

              Parameters

                     o crew_type (CrewType) - the crew type.

                     o group_mode (GroupMode) - the group mode.

              Return type
                     str

       class FindableType
              The type of an object which can be found in a movie list.

              MOVIES = 'movies'
                     Each object represents a single movie from the list.

              PEOPLE = 'people'
                     Each object may represent more than one person from the list, if grouping is enabled. They can also be limited to a specific crew type and  group
                     mode.

              ROLES = 'roles'
                     Each object represents an appearance of a person (or several grouped people) in a specific film in some specific capacity.  Think "Cristoph Waltz
                     as a castmember in Inglorious Basterds".

                     So when searching for roles, you will see an entry per people per movie.

              is_applicable_to(find: FindableType) -> bool
                     True if attributes of this findable type can be extracted from objects of type find.

                     Movie objects only support movie attributes.

                     People objects only support people attributes.

                     Roles are a combination of a movie and a people, so they support all attribute types.

                     Parameters
                            find (FindableType) - the type of the object you would try to extract an attribute from.

                     Return type
                            bool

       class Findable
              Base class for "findables"; objects which can be found in a movie list.

              property movie_list: MovieList
                     The list this object came from.

              abstract property type_: FindableType
                     The type of this object.

              abstract property uid: str
                     A unique string identifying this object. It's unique only per movie list.

              extract(attribute: Attribute) -> AttributeValue
                     Get the value of an attribute which is applicable to this object. See FindableType.is_applicable_to().

                     Parameters
                            attribute (Attribute) - the attribute whose value you are interested in.

                     Return type
                            AttributeValue

              __getitem__(attribute_name: str) -> AttributeValue
                     Same as extract(), but resolves the attribute from its name.

                     Parameters
                            attribute_name (str) - the name of the attribute. It does not have to be a qualified name.

                     Return type
                            AttributeValue

       class Movie
              Represents a movie from the list.

              property type_: FindableType
                     The type of this object.

              property uid: str
                     A unique string identifying this object. It's unique only per movie list.

              property underlying_file_movie_readonly: MLFMovie
                     Serializable object we use to store all the movie's data to disk. It can technically be modified, but you shouldn't do that.

                     NOTE:
                        This is mostly an internal API; you might need it when implementing a custom extension.

                        For typical use cases, you should use attributes to read the movie's data instead.

              associated_people(crew_type: CrewType, group_mode: GroupMode) -> Iterable[People]
                     Iterate over all people who were in this movie in a specific capacity. The order of iteration is guaranteed to be consistent.

                     Parameters

                            o crew_type (CrewType) - only find people who performed this specific job on the movie

                            o group_mode (GroupMode) - indicates if collaborators should be grouped together

                     Return type
                            Iterable[People]

              associated_roles(crew_type: CrewType, group_mode: GroupMode) -> Iterable[Role]
                     Same as associated_people(), but returns them as Role objects where the movie is this movie.

                     Parameters

                            o crew_type (CrewType)

                            o group_mode (GroupMode)

                     Return type
                            Iterable[Role]

       class PeopleUidParts
              The parts which make up a People UID. Returned by People.decompose_uid().

              mlf_people_uids: Iterable[str]

              crew_type: CrewType

              group_mode: GroupMode

       class People
              Represents a person in their capacity as a specific crew type, or several collaborating people if grouping is enabled.

              property type_: FindableType
                     The type of this object.

              property uid: str
                     A unique string identifying this object. It's unique only per movie list.

              property crew_type: CrewType
                     The job these people perform in movies.

              property group_mode: GroupMode
                     Whether these people were searched with grouping enabled.

              property underlying_file_people_readonly: list[MLFPerson]
                     List  of  serializable  objects we use to store each person's data to disk, sorted in a consistent order. It can technically be modified, but you
                     shouldn't do that.

                     NOTE:
                        This is mostly an internal API; you might need it when implementing a custom extension.

                        For typical use cases, you should use attributes to read the people's data instead.

              associated_movies() -> Iterable[Movie]
                     Iterate over movies these people were in as this crew type. The order of iteration is guaranteed to be consistent.

                     In the case of several grouped people, only movies that they were all in together will be returned.

                     Return type
                            Iterable[Movie]

              associated_roles() -> Iterable[Role]
                     Same as associated_movies(), but returns them as Role objects where the people are these people.

                     Return type
                            Iterable[Role]

              minimal_superset_people_in_other_crew_type(crew_type: CrewType) -> None | People
                     Returns the smallest group of people in another crew type who contain every person in this group, or None if there is no such group.

                     Parameters
                            crew_type (CrewType) - which crew type to search for these people in.

                     Return type
                            None | People

              minimal_superset_people_in_other_list(other_list: MovieList, crew_type: None | CrewType = None) -> None | People
                     Returns the smallest group of people in another movie list and optionally different crew type who contain every person in this group, or None  if
                     there is no such group.

                     Parameters

                            o other_list (MovieList) - which movie list to search for these people in.

                            o crew_type (None | CrewType) - which crew type to search for these people in. By default searches in the same crew type as these people.

                     Return type
                            None | People

              are_in_movie(movie: Movie) -> bool
                     Check if these people were in this movie. This takes the crew type into consideration.

                     Parameters
                            movie (Movie) - the movie to check.

                     Return type
                            bool

              classmethod compose_uid(mlf_people_uids: Iterable[str], crew_type: CrewType, group_mode: GroupMode) -> str
                     Compose a people UID which can be used to MovieList.get_people_by_uid().

                     Parameters

                            o mlf_people_uids (Iterable[str]) - the UID of each person in the source that the movie list was fetched from.

                            o crew_type (CrewType) - the people's crew type.

                            o group_mode (GroupMode) - the people's group mode.

                     Return type
                            str

              classmethod decompose_uid(uid: str) -> PeopleUidParts
                     Inverse of compose_uid().

                     Parameters
                            uid (str)

                     Return type
                            PeopleUidParts

       class RoleUidParts
              The parts which make up a Role UID. Returned by Role.decompose_uid().

              movie_uid: str

              people_uid: str

       type MLFRolesDict = dict[str, dict[CrewType, MLFRole]]
              Data  structure  with  all the raw fetch information about a role.  It maps the "MLF" UID of people in the group (i.e., the UID of a person in the fetch
              source) and the crew type to the object with the role data.

              If the Role's crew type isn't CrewType.ANY, then that'll be the only crew type in the dictionary.  Otherwise, the dictionary will have every  crew  type
              the person occupied.

                 # mlf_person is a person's underlying file object, and crew_type is the crew type he was in in this role.
                 mlf_role = roles_dict[mlf_person.uid][crew_type]
                 print(mlf_role.characters)

       class Role
              Represents  an appearance of a person (or several grouped people) in a specific film in some specific capacity. Think "Cristoph Waltz as a castmember in
              Inglorious Basterds".

              property type_: FindableType
                     The type of this object.

              property uid: str
                     A unique string identifying this object. It's unique only per movie list.

              property movie: Movie
                     The movie this role was in.

              property people: People
                     The people who performed this role.

              property crew_type: CrewType
                     The job these people performed in this movie.

              property group_mode: GroupMode
                     Whether these people were grouped by collaborators.

              property underlying_file_roles_readonly: MLFRolesDict
                     Dictionary of serializable objects we use to store each role's data to disk. It can technically be modified, but you shouldn't do that.

                     NOTE:
                        This is mostly an internal API; you might need it when implementing a custom extension.

                        For typical use cases, you should use attributes to read the role's data instead.

              classmethod compose_uid(movie: Movie, people: People) -> str
                     Compose a role UID which can be used to MovieList.get_role_by_uid().

                     Parameters

                            o movie (Movie) - the movie the role was in.

                            o people (People) - the people in the role.

                     Return type
                            str

              classmethod decompose_uid(uid: str) -> RoleUidParts
                     Inverse of compose_uid().

                     Parameters
                            uid (str)

                     Return type
                            RoleUidParts

       class MovieList
              Represents a movie list with functions to inspect the objects in the list.

              property underlying_file_readonly: MovieListFile
                     Serializable object we use to store all the list's data to disk. It can technically be modified, but you shouldn't do that.

                     NOTE:
                        This is mostly an internal API; you might need it when implementing a custom extension.

                        For typical use cases, you should use find() to read the list's data instead.

              property ctx: FlamContext
                     The context used to load this list.

              property abstract_listdef: CanonListdef
                     Listdef which describes how to get this list.

              property uid_family: str
                     The UID family used to fetch this list or all lists compositing it.  I.e., if you have multiple ways to fetch data from IMDb, they would all have
                     the same family and be compatible with one another for compositing.

              find(what: FindableType, crew_type: None | CrewType = None, group_mode: GroupMode = GroupMode.DEFAULT, filter: None | Filter = None) -> Iterable[-
              Findable]
                     Iterate over objects in the list. They're guaranteed to be returned in a consistent order every time.

                     Parameters

                            o what (FindableType) - which objects to find.

                            o crew_type (None | CrewType) - limit the search to a crew type. This has no meaning when finding FindableType.MOVIES, but is required for
                              FindableType.PEOPLE or FindableType.ROLES.

                            o group_mode (GroupMode) - specify whether to group collaborators. This has no meaning when finding FindableType.MOVIES, and  is  optional
                              for the rest.

                            o filter (None | Filter) - skip objects which don't pass this filter. It must have the same findable type as what.

                     Return type
                            Iterable[Findable]

              find_movies(filter: None | Filter = None) -> Iterable[Movie]
                     Wrapper for find() when searching for FindableType.MOVIES.

                     Parameters
                            filter (None | Filter)

                     Return type
                            Iterable[Movie]

              find_people(crew_type: CrewType, group_mode: GroupMode = GroupMode.DEFAULT, filter: None | Filter = None) -> Iterable[People]
                     Wrapper for find() when searching for FindableType.PEOPLE.

                     Parameters

                            o crew_type (CrewType)

                            o group_mode (GroupMode)

                            o filter (None | Filter)

                     Return type
                            Iterable[People]

              find_roles(crew_type: CrewType, group_mode: GroupMode = GroupMode.DEFAULT, filter: None | Filter = None) -> Iterable[Role]
                     Wrapper for find() when searching for FindableType.ROLES.

                     Parameters

                            o crew_type (CrewType)

                            o group_mode (GroupMode)

                            o filter (None | Filter)

                     Return type
                            Iterable[Role]

              get_by_uid(findable_type: FindableType, uid: str) -> None | Findable
                     Get an object by its UID, or None if it doesn't exist.

                     Parameters

                            o findable_type (FindableType) - the type of object to get.

                            o uid (str) - the object's UID.

                     Return type
                            None | Findable

              get_movie_by_uid(uid: str) -> None | Movie
                     Wrapper for get_by_uid() when searching for FindableType.MOVIES.

                     Parameters
                            uid (str)

                     Return type
                            None | Movie

              get_people_by_uid(uid: str, ct_gm_hint: None | tuple[CrewType, GroupMode] = None) -> None | People
                     Wrapper for get_by_uid() when searching for FindableType.PEOPLE.

                     Parameters

                            o ct_gm_hint  (None | tuple[CrewType, GroupMode]) - the people's crew type and group mode. This is optional because it can be deduced from
                              uid, but it helps performance.

                            o uid (str)

                     Return type
                            None | People

              get_role_by_uid(uid: str, ct_gm_hint: None | tuple[CrewType, GroupMode] = None) -> None | Role
                     Wrapper for get_by_uid() when searching for FindableType.ROLES.

                     Parameters

                            o ct_gm_hint (None | tuple[CrewType, GroupMode]) - the role's crew type and group mode. This is optional because it can  be  deduced  from
                              uid, but it helps performance.

                            o uid (str)

                     Return type
                            None | Role

       type AttributePrimitive = None | SupportsRichComparison
              Type  of  a  single element from a value returned by an attribute. Attributes may return either a primitive or a list of primitives.  None is common, so
              always check for that. And other than that you are guaranteed that the value will be sortable.

       type AttributeValue = AttributePrimitive | list[AttributePrimitive]
              Type of a value returned by an attribute.

       class ComparisonOp
              Enumeration of possible comparison operators for comparing attributes to values. Each operator is represented with a different sign.

              LE = '-'
                     Less or equal.

              GE = '+'
                     Greater or equal.

              EQ = '='
                     Exactly equal.

              LT = '.-'
                     Strictly less than.

              GT = '.+'
                     Strictly greater than.

              RX = '~'
                     str(value) matches a regular expression.

              __call__(primitive_lhs: AttributePrimitive, primitive_rhs: AttributePrimitive | Pattern) -> bool
                     Compare the left hand side to the right hand side. The order matters. To illustrate:

                        # Same as 10 < 15.
                        ComparisonOp.LE(10, 15)

                        # Same as re.search('the.*', 'The Big Lebowski').
                        ComparisonOp.RX('The Big Lebowski', re.compile('the.*'))

                     Parameters

                            o primitive_lhs (AttributePrimitive) - the left hand side value to compare.

                            o primitive_rhs (AttributePrimitive | Pattern) - the right hand side value to compare.

                     Return type
                            bool

       class CmpTo
              Represents comparison of some attribute's values to some constant primitive value, using a specific comparison operator.

              In a filter, these would be represented as a string like so: `<op><value>', where <op> is the sign of a ComparisonOp, and <value> is a possible value of
              an attribute.  The <op> part is optional as all attributes define a default operator. To illustrate:

                 # As a string: '+90'. Checks if metascores are greater or equal to 90.
                 CmpTo(ComparisonOp.GE, 90, ctx.attributes['movies-metascore'])

                 # As a string: '~the.*'. Checks if stringified values match the regex 'the.*'.
                 # This is usually the default operator for string attributes, so the '~' can usually be omitted and you can simply write 'the.*'.
                 CmpTo(ComparisonOp.RX, re.compile('the.*'), ctx.attributes['movies-title'])

              __init__(op: ComparisonOp, const_primitive: AttributePrimitive | Pattern, attribute: Attribute) -> None

                     Parameters

                            o op (ComparisonOp) - the operator to use for comparisons.

                            o const_primitive (AttributePrimitive | Pattern) - which constant value to compare other values to.

                            o attribute (Attribute) - which attribute the values are expected to come from.

                     Return type
                            None

              __call__(primitive: AttributePrimitive) -> bool
                     Compares primitive to the constant value. Expects primitive to come from the same attribute this object was created with.

                     When comparing None to a not-None value, the result is always false. That is, None is neither less, nor greater, nor equal to other values.

                     Parameters
                            primitive (AttributePrimitive) - variable value to compare with.

                     Return type
                            bool

       class Attribute
              Base class for all attributes that a findable object may have. This is a key element in interfacing with flam - any data you may be  interested  in  ob-
              taining about a movie, people, or role is obtained via attributes.

              Attributes provide facilities for using them generically.  You can extend flam by inheriting from this class and registering your own custom attributes.

              NONE_STR = '-'
                     The string representation we use for None values.

              __init__(findable_type: FindableType, name_without_type: str, aliases_without_type: None | list[str] = None)

                     Parameters

                            o findable_type (FindableType) - the type of objects which have this attribute.

                            o name_without_type (str) - the name of the attribute without the findable type.

                            o aliases_without_type (None | list[str]) - list of aliases for the attribute, also without the type.

              property name_without_type: str
                     The name of the attribute without the findable type. E.g. `title', not `movies-title'.

              property aliases_without_type: Iterable[str]
                     Iterate over all aliases of the attribute, also without the findable type.

              property findable_type: FindableType
                     The type of objects which have this attribute.

              property qualified_name: str
                     The qualified name of the attribute. E.g. `movies-title', not just `title'.

              property qualified_aliases: Iterable[str]
                     Iterate over all aliases of the attribute, by their qualified name.

              abstract property is_ascending: bool
                     Suggestion for whether you should sort this attribute in ascending or descending order.

              abstract property default_op: ComparisonOp
                     The default comparison operator that makes sense for this attribute.

                     Typically, string attributes use regular expression matching by default, and other attributes check for equality.

              abstractmethod _parse_primitive_not_none(primitive_str: str) -> AttributePrimitive

                     NOTE:
                        This  is  an internal method of attributes. Outside users shouldn't call it, but you need to implement it as part of implementing a custom at-
                        tribute.

                     Parse a string representing a single primitive into the value of this attribute. You do not need to handle None or lists in this function.

                     Parameters
                            primitive_str (str) - a string representation of a single primitive value. You may assume this is not NONE_STR.

                     Return type
                            AttributePrimitive

              abstractmethod _str_of_primitive_not_none(primitive: AttributePrimitive, abbreviate: bool, extras: dict[str, Any]) -> str

                     NOTE:
                        This is an internal method of attributes. Outside users shouldn't call it, but you need to implement it as part of implementing a  custom  at-
                        tribute.

                     Return a string representation of a single primitive value of this attribute. You do not need to handle None or lists in this function.

                     If abbreviate is false, then this method must be the inverse of _parse_primitive_not_none().

                     Parameters

                            o primitive (AttributePrimitive) - a single primitive value. You may assume this is not None.

                            o abbreviate (bool) - indicates if the string should be abbreviated. For example truncating long strings, or converting 1000000 to "1M".

                            o extras (dict[str, Any]) - additional optional arguments to control the string conversion.

                     Return type
                            str

              sort_key(value: AttributeValue, is_ascending: None | bool = None) -> Any
                     Returns an object which may be used to safely compare values of this attribute, even if some of them are None. For example:

                     Parameters

                            o is_ascending  (None | bool) - optionally indicate the desired sort order. This will ensure Nones are at the bottom of the sort no matter
                              what. Defaults to is_ascending.

                            o value (AttributeValue)

                     Return type
                            Any

                        attr_values = [movie.extract(attr) for attr in movie_list.find_movies()]
                        attr_values.sort(key=attr.sort_key, reverse=(not attr.is_ascending))

                     Parameters

                            o value (AttributeValue) - a value extracted using this attribute.

                            o is_ascending (None | bool)

                     Return type
                            Any

              parse_primitive(primitive_str: str) -> AttributePrimitive
                     Parse a string representing a single primitive into the value of this attribute.

                     Parameters
                            primitive_str (str) - a string representation of a single primitive value.

                     Return type
                            AttributePrimitive

              str_of_value(value: AttributeValue, abbreviate: bool = False, **extras: Any) -> str
                     Return a string representation of a value of this attribute. If the value is a list, its elements will be separated by commas.

                     Parameters

                            o value (AttributeValue) - a value of this attribute.

                            o abbreviate (bool) - indicates if the string should be abbreviated. For example truncating long strings, or converting 1000000 to "1M".

                            o extras (Any) - additional optional arguments to control the string conversion.

                     Return type
                            str

       class CanonListdef
              Represents a spec for identifying a list. Listdefs as a strings have the form "<list_type>=<address>", or sometimes just "<address>", and internally  we
              "canonicalize" them into this object. This process involves:

              o Inferring the list type if it's missing - we're only able to infer the types for configured simple or composite lists

              o Handling some special values. See SpecialListType.

              Some important things to know about listdefs:

              o Once  they're  past  canonicalization, the special list types SpecialListType.ALL and SpecialListType.DEFAULTS are handled and you don't have to check
                for them

              o Simple/composite lists are described with the address being their name, but during canonicalization we change that to their UID

              o When printing a listdef to the user, it's best to format it pretty() because that will convert these list uids back to their names

              Concrete and abstract canon listdefs:

              o Canon listdefs are "concrete" when they describe the raw address from which the data was fetched. E.g., "imdb-listid=083886771"

              o Canon listdefs are "abstract" when they describe a configured list. E.g., "list=watched"

              list_type: str
                     The fetcher used to download information about this list. Also supports a few special values, see SpecialListType.

              address: str
                     An address pointing to an exact list. This could be different based on the list type - it could be a path, a URL, a name, or anything else.

              property is_special: bool
                     Whether this listdef has a special type.

              property is_abstract: bool
                     Whether this listdef is "abstract", meaning it describes a configured list. E.g., "list=watched".

              property is_concrete: bool
                     Whether this listdef is "concrete", meaning it describes a raw address from which the data was fetched. E.g., "imdb-listid=083886771".

              pretty(ctx: FlamContext) -> str
                     Returns a pretty string representation of this listdef. Use this so configured lists will be printed with their name instead of a long ugly UID.

                     Parameters
                            ctx (FlamContext) - the context containing list configurations we need to know.

                     Return type
                            str

       class SpecialListType
              An enumeration of special listdef types.

              ALL = '*'
                     All configured simple lists.

              DEFAULTS = 'defaults'
                     All configured as default lists. We have different defaults for fetch vs find.

              SIMPLE = 'list'
                     A simple list. Lists which are just a name for the raw data list from the source. Outwardly uses the name as the address, but internally  uses  a
                     uid.

              COMPOSITE = 'composite'
                     A composite list. Lists which are a combination of other lists with a filter. Outwardly uses the name as the address, but internally uses a uid.

              ANONYMOUS = 'anonymous'
                     FOR INTERNAL USE ONLY - anonymous composites. Composite lists which are not preconfigured but spun on-the-fly.

       register(item: T) -> T
              Register an predicate, attribute, or fetcher as a global extension. Global extensions are available to use from any context with import_extensions=True.

              You may register an item with the same name as that of a builtin, and it will shadow it.

              This function is meant to be used as a decorator (i.e. with @register).

              Parameters
                     item (T) - the item to register.

              Return type
                     T

       compose_qualified_attr_or_pred_name(findable_type: FindableType, name_without_type: str) -> str
              Compose an attribute or predicate qualified name from its parts. For example:

                 compose_qualified_attr_or_pred_name(FindableType.MOVIES, 'title') # Returns 'movies-title'.

              Parameters

                     o findable_type (FindableType) - the item's type.

                     o name_without_type (str) - the item's name without the type.

              Return type
                     str

       decompose_qualified_attr_or_pred_name(qualified_name: str) -> tuple[FindableType, str]
              Inverse of compose_qualified_attr_or_pred_name().

              Parameters
                     qualified_name (str)

              Return type
                     tuple[FindableType, str]

       class Fetcher
              Base class for all fetchers. Fetchers are in charge of actually downloading data about movie lists from some source like IMDb, Letterboxd, etc.

              You can extend flam by inheriting from this class and registering your own custom fetchers.

              qualified_name: str
                     The fetcher's name. This corresponds to a listdef's list_type.

              qualified_aliases: list[str]
                     List of aliases for the fetcher.

              uid_family: str
                     The UID family this fetcher uses. Composite lists can only be formed from lists with the same UID family.

                     Fetchers  need  to  assign some unique string to each movie and person they download, but what exactly they use can vary from fetcher to fetcher.
                     Usually they'll use the UIDs used by the source the data is fetched from.

                     For example, a fetcher which sources its data from IMDb might identify a movie by the same ID IMDb identifies it with.  But  there  are  multiple
                     APIs you could use to fetch data from IMDb. So you can implement multiple fetchers which all fetch from IMDb, but in different ways.  If you make
                     all those fetchers use the same UID family, then they will all be compatible with each other.

              classmethod __init_subclass__(list_type: str, qualified_aliases: None | list[str] = None, uid_family: None | str = None, **kwargs: Any) -> None
                     Defines parameters that subclasses can (or must) pass in as part of subclassing. Ex:

                        class MyCustomFetcher(Fetcher, list_type='my-imdb-fetcher', uid_family='imdb'):
                            # ...

                     Parameters

                            o list_type (str) - the name of this fetcher.

                            o qualified_aliases (None | list[str]) - list of aliases for this fetcher.

                            o uid_family (None | str) - which UID family this fetcher uses. Defaults to list_type, indicating this fetcher is only compatible with it-
                              self.

                            o kwargs (Any)

                     Return type
                            None

              __init__(concrete_listdef: CanonListdef, abstract_listdef: CanonListdef, fetch_params: dict[str, str], ctx: FlamContext) -> None

                     Parameters

                            o concrete_listdef (CanonListdef)

                            o abstract_listdef (CanonListdef)

                            o fetch_params (dict[str, str])

                            o ctx (FlamContext)

                     Return type
                            None

              property concrete_listdef: CanonListdef
                     The raw fetcher name and address to fetch from.

              property abstract_listdef: CanonListdef
                     The simple list being fetched, if it is a simple list. Otherwise same as concrete_listdef.

              get_param(param_name: str) -> str
                     Get the value of a fetch parameter.

                     Parameters
                            param_name (str)

                     Return type
                            str

              has_param(param_name: str) -> bool
                     Check if fetcher has been passed this parameter.

                     Parameters
                            param_name (str)

                     Return type
                            bool

              abstractmethod _fetch_into_file(movie_list_file: MovieListFile) -> None

                     NOTE:
                        This  is  an  internal  method  of  fetchers.  Outside  users shouldn't call it, but you need to implement it as part of implementing a custom
                        fetcher.

                     Obtain data about the list identified by concrete_listdef, and populate movie_list_file with all the data fetched about movies in  the  list  and
                     the people in them.

                     If this function raises FetchInterrupt, everything written to the file so far will be saved to disk.

                     There are a few sensitive points about the exact order that you populate this object with data:

                     o Always add the people in a movie before you add the movie itself

                     o Only add a movie object after it's fully populated with its data

                     The  above  points  ensure  that the file is always in a good, saveable state. So if fetching gets interrupted or _checkpoint() is called and the
                     file is saved, it won't cause partially fetched movies to be in the file, which on the next fetch you won't know you have to re-fetch.

                     Parameters
                            movie_list_file (MovieListFile) -

                            serializable object to populate with all the data we can get about the movie list.

                            The only fields you should populate are movies_by_uid, people_by_uid. The rest are handled outside this fetcher.

                            If the list was previously fetched, this object will contain all the data from the previous fetch.  Use this fact  to  only  fetch  movies
                            that weren't already fetched, which can save a lot of time.

                            However, it's also your responsibility to remove movies from this file if they are no longer in the list.

                            When  removing  movies that were previously fetched, you don't have to worry about also removing the people in those movies.  That happens
                            automatically before the file is saved.

                     Return type
                            None

              _checkpoint(movie_list_file: MovieListFile) -> None
                     Store the work-in-progress on this file to disk, so that if a crash happens the data fetched so far won't be lost.  This method is meant to  only
                     be called internally from inside _fetch_into_file().

                     You should only call this function during good save points. I.e., moments when the file doesn't contain any partially fetched movies.

                     Parameters
                            movie_list_file (MovieListFile) - the same object that was passed to _fetch_into_file().

                     Return type
                            None

       class EatParams
              Common parameters passed around during filter "eating" (i.e. compilation).

              tokens: list[str]
                     The complete list of tokens to compile, including ones already processed.

              find: FindableType
                     The  type of objects this filter filters. Some predicates are specific to a findable type, so some filters will compile for one findable type but
                     not another.

              ctx: FlamContext
                     Context that this filter came from. Some filters will only compile with a specific context in which you've registered predicates  other  contexts
                     don't know about.

              __init__(tokens: list[str], find: FindableType, ctx: FlamContext) -> None

                     Parameters

                            o tokens (list[str])

                            o find (FindableType)

                            o ctx (FlamContext)

                     Return type
                            None

       class FilterMember
              Base class for all components of a filter. Filters are essentially an AST (abstract syntax tree) of filter members.

              abstractmethod excrete(findable: Findable) -> bool
                     Returns True if the findable passes this filter.

                     Parameters
                            findable (Findable) - the object to filter.

                     Return type
                            bool

              abstractmethod regurgitate() -> Iterable[str]
                     Decompiles this filter into a list of tokens.

                     Return type
                            Iterable[str]

              abstractmethod colonoscopy() -> Iterable[FilterMember]
                     Iterate over all members of the filter. They're returned from left to right.

                     Return type
                            Iterable[FilterMember]

              classmethod eat_str(params: EatParams, at: int, description: str, error_indices: int | Iterable[int] = -1, is_terminal: bool = True) -> str
                     Helper method to eat and return params.tokens[at], or raise FilterSyntaxError if the index is out of bounds.

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - the index of the token to eat.

                            o description (str) - describes the expected meaning of this token, for raising a meaningful exception (ex: `a valid attribute name').

                            o error_indices (int | Iterable[int]) - indicates which indices in params.tokens to highlight as problematic in case of error.

                            o is_terminal (bool) - indicates if an exception would mean that the entire compilation has failed, or is the error recoverable.

                     Return type
                            str

              classmethod eat_listof(eatfunc: Callable[[EatParams, int], T], params: EatParams, at: int, at_least_one: bool) -> tuple[list[T], int]
                     Helper  method  to eat and return a list of tokens. Returns a tuple of the list and the index of the first token after the list. The syntax is as
                     follows:

                        o List of 0 or more elements enclosed in parentheses:
                              `-any-role [ director writer composer ] -name tarantino'

                        o List of size 1, no parentheses required:
                              `-any-role director -name tarantino'

                        o Empty list, both parentheses can be a single token:
                              `-any-role [] -name tarantino'

                     Example of eating a list of CTGMs:

                            ct_gms, until = FilterMember.eat_listof(FilterMember.eat_ct_gm, params, at, at_least_one=False)

                     Parameters

                            o eatfunc (Callable[[EatParams, int], T]) - function which should eat only a single token at at a given index. Each list element  will  be
                              consumed with this function.

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - index at which the list tokens are expected to begin.

                            o at_least_one (bool) - indicates if this function should fail on empty lists.

                     Return type
                            tuple[list[T], int]

              classmethod eat_one_of(params: EatParams, at: int, description: str, options: set[str], is_terminal: bool = True) -> str
                     Helper method to eat and return a single token which must be one of a set of possible values.

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - the index of the token to eat.

                            o description (str) - describes the expected meaning of this token, for raising a meaningful exception (ex: `left parenthesis').

                            o options (set[str]) - set of possible values the token is allowed to have.

                            o is_terminal (bool) - indicates if an exception would mean that the entire compilation has failed, or is the error recoverable.

                     Return type
                            str

              classmethod eat_attribute(params: EatParams, at: int) -> Attribute
                     Helper method to eat a token representing an attribute name and return the attribute.

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - the index of the token to eat.

                     Return type
                            Attribute

              classmethod eat_cmpto(params: EatParams, at: int, attribute: Attribute) -> CmpTo
                     Helper method to eat a token representing a CmpTo and return it.

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - the index of the token to eat.

                            o attribute (Attribute) - the attribute whose values are to be compared.

                     Return type
                            CmpTo

              classmethod eat_type(params: EatParams, at: int, description: str, type_: Callable[[str], T]) -> T
                     Helper method to eat a token which should be parseable as some specific type, and return the parsed value.

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - the index of the token to eat.

                            o description (str) - describes the expected meaning of this token, for raising a meaningful exception (ex: `a valid attribute name').

                            o type_ (Callable[[str], T]) - function for parsing the token string. E.g., int, CrewType.

                     Return type
                            T

              classmethod eat_movie_list(params: EatParams, at: int) -> tuple[MovieList, int]
                     Helper  method to eat a list of tokens representing listdefs which together compose a MovieList.  Returns a tuple of the movie list and the index
                     of the first token after the list.

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - index at which the listdef tokens are expected to begin.

                     Return type
                            tuple[MovieList, int]

              classmethod eat_ct_gm(params: EatParams, at: int) -> tuple[CrewType, GroupMode]
                     Helper method to eat a token representing a CrewType and optional GroupMode. See parse_ct_gm().

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - the index of the token to eat.

                     Return type
                            tuple[CrewType, GroupMode]

              classmethod eat_subfilter(params: EatParams, at: int) -> tuple[Filter, int]
                     Helper method to eat a list of tokens representing a subfilter that is either made up of a single member or enclosed in parentheses.   Returns  a
                     tuple of the subfilter and the index of the first token after the filter.

                     Parameters

                            o params (EatParams) -

                              general parameters of this compilation. Sometimes subfilters may have a different EatParams.find than the parent filter. For example:

                                 sub_params = dataclasses.replace(params, find=FindableType.ROLES)
                                 sub_filter, until = FilterMember.eat_subfilter(sub_params, at)

                            o at (int) - index at which the subfilter tokens are expected to begin.

                     Return type
                            tuple[Filter, int]

       class Filter
              Represents the root member of a filter.

              property findable_type: FindableType
                     The  type of objects this filter filters. Some predicates are specific to a findable type, so some filters will compile for one findable type but
                     not another.

              property ctx: FlamContext
                     Context that this filter came from. Some filters will only compile with a specific context in which you've registered predicates  other  contexts
                     don't know about.

              property is_empty: bool
                     True if this filter checks nothing.

              excrete(findable: Findable) -> bool
                     Returns True if the findable passes this filter.

                     Parameters
                            findable (Findable) - the object to filter.

                     Return type
                            bool

              regurgitate() -> Iterable[str]
                     Decompiles this filter into a list of tokens.

                     Return type
                            Iterable[str]

              colonoscopy() -> Iterable[FilterMember]
                     Iterate over all members of the filter. They're returned from left to right.

                     Return type
                            Iterable[FilterMember]

       class Pipeline
              Represents a sequence of filter members combined with an implicit logical AND.

              LPAREN = {'(', '-lparen', '['}
                     Set of accepted strings representing a left parenthesis. Use min(LPAREN) as the representative for regurgitation.

              RPAREN = {')', '-rparen', ']'}
                     Set of accepted strings representing a right parenthesis. There is no requirement for the left and right parentheses to use matching strings.

              BOTHPAREN = {'()', '[]'}
                     Set of accepted strings representing an empty list.

              excrete(findable: Findable) -> bool
                     Returns True if the findable passes this filter.

                     Parameters
                            findable (Findable) - the object to filter.

                     Return type
                            bool

              regurgitate() -> Iterable[str]
                     Decompiles this filter into a list of tokens.

                     Return type
                            Iterable[str]

              colonoscopy() -> Iterable[FilterMember]
                     Iterate over all members of the filter. They're returned from left to right.

                     Return type
                            Iterable[FilterMember]

       class Negative
              Represents a single filter member whose result is logically negated (true becomes false, false becomes true).

              NEGATE = {'!', '-n', '-not'}
                     Set of accepted strings representing "not".

              excrete(findable: Findable) -> bool
                     Returns True if the findable passes this filter.

                     Parameters
                            findable (Findable) - the object to filter.

                     Return type
                            bool

              regurgitate() -> Iterable[str]
                     Decompiles this filter into a list of tokens.

                     Return type
                            Iterable[str]

              colonoscopy() -> Iterable[FilterMember]
                     Iterate over all members of the filter. They're returned from left to right.

                     Return type
                            Iterable[FilterMember]

       class Conjoined
              Represents  a  single  fitler member combined to its previous member with logical AND. This class is never actually instantiated, since members are com-
              bined with AND by default.

              CONJOIN = {'&', '-a', '-and'}
                     Set of accepted strings representing "and".

       class Disjoined
              Represents a single fitler member combined to its previous member with logical OR.

              DISJOIN = {'-o', '-or', '|'}
                     Set of accepted strings representing "or".

              excrete(findable: Findable) -> bool
                     Returns True if the findable passes this filter.

                     Parameters
                            findable (Findable) - the object to filter.

                     Return type
                            bool

              regurgitate() -> Iterable[str]
                     Decompiles this filter into a list of tokens.

                     Return type
                            Iterable[str]

              colonoscopy() -> Iterable[FilterMember]
                     Iterate over all members of the filter. They're returned from left to right.

                     Return type
                            Iterable[FilterMember]

       class Predicate
              Base class for all predicates - filter members which check something about a findable object.

              You can extend flam by inheriting from this class and registering your own custom predicates.

              PREFIX = '-'
                     All predicate names in a filter should begin with this string.

              qualified_name: str
                     The predicate's name (not including PREFIX). For predicates specific to some findable type, this also includes the type (e.g., `movies-any-role',
                     not `any-role').

              qualified_aliases: list[str]
                     List of aliases for the predicate.

              findable_type: None | FindableType
                     Which type of objects this predicate is for. If this is None, all types are supported.

              classmethod __init_subclass__(name_without_type: str, aliases_without_type: None | list[str] = None, findable_type: None | FindableType = None,
              **kwargs: Any) -> None
                     Defines parameters that subclasses can (or must) pass in as part of subclassing. Ex:

                        class MyCustomPredicate(Predicate, name_without_type='my-predicate', findable_type=FindableType.MOVIES):
                            # ...

                     Parameters

                            o name_without_type (str) - the name of the predicate without the findable type.

                            o qualified_aliases - list of aliases for the predicate, also without the type.

                            o findable_type (None | FindableType) - which type of objects this predicate is for, or None if it's for all types.

                            o aliases_without_type (None | list[str])

                            o kwargs (Any)

                     Return type
                            None

              classmethod eat(params: EatParams, at: int) -> tuple[Predicate, int]
                     Eats tokens which are arguments to this predicate starting from params.tokens[at]. Returns the consumed predicate and the index of the first  to-
                     ken after it.

                     Subclasses must override this with their own custom logic. You don't have to worry about eating the predicate name itself. For example:

                        @classmethod
                        def eat(cls, params: EatParams, at: int) -> tuple[Predicate, int]:
                            # This predicate takes an attribute and a CMPTO as its arguments.
                            attribute = cls.eat_attribute(params, at)
                            cmpto = cls.eat_cmpto(params, at + 1, attribute)
                            return cls(attribute, cmpto), at + 2

                     Parameters

                            o params (EatParams) - general parameters of this compilation.

                            o at (int) - index at which the predicate tokens are expected to begin.

                     Return type
                            tuple[Predicate, int]

              excrete(findable: Findable) -> bool
                     Returns True if the findable passes this filter.

                     Parameters
                            findable (Findable) - the object to filter.

                     Return type
                            bool

              regurgitate() -> Iterable[str]
                     Decompiles this filter into a list of tokens.

                     Return type
                            Iterable[str]

              colonoscopy() -> Iterable[FilterMember]
                     Iterate over all members of the filter. They're returned from left to right.

                     Return type
                            Iterable[FilterMember]

       looks_like_filter_token(s: str) -> bool
              Checks  if a string matches one of the known forms of a filter token. Use this to split an args list where you expect a filter to start somewhere in the
              middle of the list.

              Parameters
                     s (str) - the string which might belong in a filter.

              Return type
                     bool

       class SimpleList
              Serializable object with configuration data about a simple list.

              uid: str

              name: str

              concrete_listdef: CanonListdef

              is_default_fetch: bool

              is_default_find: bool

              fetch_params: dict[str, str]

              property abstract_listdef: CanonListdef

       class CompositeList
              Serializable object with configuration data about a composite list.

              uid: str

              name: str

              simple_list_uids: list[str]

              filter_tokens: list[str]

              is_default_find: bool

              property abstract_listdef: CanonListdef

       class Configuration
              Serializable object with all the configuration data.

              version: str

              simple_lists_raw: list[SimpleList]

              composite_lists_raw: list[CompositeList]

              extensions: list[str]

              property simple_lists: ConfigurationLists[SimpleList]
                     Data structure of all simple lists with some niceities that aren't serialized. Prefer this over simple_lists_raw.

              property composite_lists: ConfigurationLists[CompositeList]
                     Data structure of all composite lists with some niceities that aren't serialized. Prefer this over composite_lists_raw.

              lists_of_type(list_type: str) -> ConfigurationLists[SimpleList] | ConfigurationLists[CompositeList]
                     Generic wrapper for composite_lists, composite_lists.

                     Parameters
                            list_type (str) - SIMPLE or COMPOSITE.

                     Return type
                            ConfigurationLists[SimpleList] | ConfigurationLists[CompositeList]

              get_list_by_abstract_listdef(abstract_listdef: CanonListdef) -> SimpleList | CompositeList
                     Returns a configured list based on the listdef.

                     Parameters
                            abstract_listdef (CanonListdef) - listdef with the type and UID of the list.

                     Return type
                            SimpleList | CompositeList

       class ConfigurationLists
              Data structure for generically interfacing with Configuration.simple_lists_raw or Configuration.composite_lists_raw.

              __iter__() -> Iterator[T]
                     Iterate over all configured lists of this type.

                     Return type
                            Iterator[T]

              get_idx_by_uid(uid: str) -> int
                     Get a list's index in the configuration using its UID.

                     Parameters
                            uid (str) - the list's UID.

                     Return type
                            int

              get_idx_by_name(name: str) -> int
                     Get a list's index in the configuration using its name.

                     Parameters
                            name (str) - the list's name.

                     Return type
                            int

              get_by_uid(uid: str) -> T
                     Get a list's configuration using its UID.

                     Parameters
                            uid (str) - the list's UID.

                     Return type
                            T

              get_by_name(name: str) -> T
                     Get a list's configuration using its name.

                     Parameters
                            name (str) - the list's name.

                     Return type
                            T

       class MLFRole
              Serializable object with data about a role in a movie.

              person_uid: str

              is_star: None | bool

              episodes_num: None | int

              oscar_noms: list[str]

              oscar_wins: list[str]

              characters: list[str]

              jobs: list[str]
                     For crew type ADDITIONAL, this describes what the roles were.

       class MLFCrew
              Serializable object with data about the crew of a specific type in a movie.

              crew_type: CrewType

              roles_by_uid: dict[str, MLFRole]

       class MLFPerson
              Serializable object with data about a person who appeared in one or more movies from the list.

              uid: str

              name: None | str

              gender: None | str
                     The exact strings representing each gender may vary based on where the data was fetched from.

              height_cm: None | float

              birthday: None | date

              deathday: None | date

              death_reason: None | str

              countries: list[str]

       class MLFMoviePerSourceData
              Serializable object with data about a movie which is specific to the list from which it came, not universal data about that movie.

              canon_listdef: CanonListdef
                     The list this data belongs to. Fetchers should make this equal to the MovieListFile.abstract_listdef.

              list_index: None | int

              list_note: None | str
                     This field is for notes specifically attached to the occurrence of the film in this list.

                     For the user's notes about the film in general, use MLFMovie.my_notes.

              listing_date: None | date
                     The date this movie was added to this list.

       class MLFMovie
              Serializable object with data about a movie.

              uid: str

              per_src_data: list[MLFMoviePerSourceData]
                     Data about a movie which is specific to the list from which it came. Fetchers should build this list with only one element.

              media_type: None | str

              title: None | str

              original_title: None | str

              tagline: None | str

              synopsis: None | str

              url: None | str

              runtime_minutes: None | int

              metascore_votes: None | int

              metascore: None | int

              votes: None | int

              rating: None | float

              my_rating: None | float

              likes: None | int

              is_liked: None | bool

              budget_usd: None | int

              revenue_usd: None | int

              content_rating: None | str

              release_date: None | date

              watch_dates: list[date]
                     The date(s) you watched this movie. It's a list in case you've seen it more than once and have that data.

              my_notes: list[str]
                     What constitutes a "note" is up to the fetcher. For instance, in Letterboxd fetcher it's the user's reviews of the film.

              episodes_num: None | int

              seasons_num: None | int

              end_date: None | date

              genres: list[str]

              studios: list[str]

              languages: list[str]

              countries: list[str]

              crew: dict[CrewType, MLFCrew]

       class MovieListFile
              Serializable object with all the data about a movie list.

              version: str

              abstract_listdef: CanonListdef

              uid_family: str
                     Files are "compatible" if they have a matching family. For instance, different IMDb fetchers might all rely on IMDb IDs, so they can all have the
                     same family.

              movies_by_uid: dict[str, MLFMovie]
                     All the movies in the list. Note that we don't support multiple occurrences of the same movie.  Fetchers are free to  choose  one  occurrence  to
                     keep at random.

              expiration_date: None | date
                     Some APIs legally require us to not cache data forever. When this date hits, the file will be deleted and everything will have to be re-fetched.

              people_by_uid: dict[str, MLFPerson]

       class FlamEnv
              Collection of environment variables used by flam.

              DEBUG = 'FLAM_DEBUG'
                     If is_truthy, flam will operate in debug mode. We're more strict with exceptions in debug mode.

              LOG2CONSOLE = 'FLAM_LOG2CONSOLE'
                     If is_truthy, logs will be printed not just to their log file but also to the console.

              LOGLEVEL = 'FLAM_LOGLEVEL'
                     Suppresses logs below this level. See the python docs on logging levels.

              CTX_DIR = 'FLAM_DIR'
                     Overrides the default path used to store files.

              DOWNLOADS_DIR = 'FLAM_DOWNLOADS'
                     Overrides the default path searched for files downloaded from the browser when fetching from IMDb.

              BROWSER = 'FLAM_BROWSER'
                     Manually decide which browser to use to download CSVs from IMDb: `chrome', `edge', or `firefox'.

              BROWSER_PROFILE = 'FLAM_BROWSER_PROFILE'
                     Path to a browser profile to open the browser with when downloading CSVs from IMDb.

                     This is only needed if your list is set to private so a profile is needed where you are expected to be already logged in.

              TMDB_API_TOKEN = 'FLAM_TMDB_API_TOKEN'
                     The API read access token granted to you by TMDB for using their API.

              property is_defined: bool
                     True if this environment variable is defined.

              property is_truthy: bool
                     True if this environment variable is defined, not empty, and not `0'.

              get_or_default(default: str = '') -> str
                     Return the value of this environment variable, or some default if it is not defined.

                     Parameters
                            default (str)

                     Return type
                            str

       is_debug() -> bool
              True if flam is in debug mode.

              Return type
                     bool

       get_log_file_path() -> str
              Returns the path where flam stores its logs:

              o Windows: %LOCALAPPDATA%/film_flam/output.log

              o Linux: ~/.local/state/film_flam/output.log

              o macOS: ~/Library/Logs/film_flam/output.log

              o On unidentified platforms, uses the current directory (./output.log)

              Return type
                     str

       logger The logger flam uses for all its logs. It is thread-safe and multiprocess-safe, and you may also use it.

       exception FlamError
              Base class for all errors raised by flam.

       exception InputError
              Base class for errors raised by flam because of bad input from the user.

       exception CloseInputError
              Input error with suggestions for similar values to help with typos.

       exception FilterSyntaxError
              Input error for filters that don't compile.

              classmethod join_tokens(tokens: list[str], error_indices: int | Iterable[int]) -> str

                     Parameters

                            o tokens (list[str])

                            o error_indices (int | Iterable[int])

                     Return type
                            str

              classmethod format_token(token: str, is_error: bool = False) -> str

                     Parameters

                            o token (str)

                            o is_error (bool)

                     Return type
                            str

       exception FetchInterrupt
              Error used to gracefully exit fetch() in the middle of work.

       exception FileValidationError
              Error indicating that a file loaded by flam was corrupted or tampered with.

   flam.utils
       This page documents the flam.utils module. It contains generic utilities used by flam which may be useful for you too.

       class ProgressBar
              Utility for iterating over a list and presenting a progress bar to the user via stdout:

                 # How you might hypothetically fetch a list of movies while presenting a progress bar about it.
                 with ProgressBar(movies_to_fetch,
                         desc='Downloading',
                         keyfunc=lambda m: m.title) as bar:
                     for movie in bar:
                         fetch_movie(movie)

              __init__(elements: list[T], desc: None | str = None, keyfunc: None | Callable[[T], str] = None) -> None

                     Parameters

                            o elements (list[T]) - list of elements to process while displaying the progress bar.

                            o desc (None | str) - short description to print to the user of what is being done.

                            o keyfunc (None | Callable[[T], str]) - function which receives an element from the list and returns a short string indicating to the user
                              which element is currently being processed.

                     Return type
                            None

              __iter__() -> Iterator[T]
                     Iterate over elements and update the progress bar with each iteration.

                     Return type
                            Iterator[T]

              __enter__() -> Self
                     Returns self. When the context exits the progress bar will be cleaned up.

                     Return type
                            Self

              __exit__(exc_type: type[BaseException], exc_value: None | BaseException, traceback: None | TracebackType) -> None
                     Cleanly ends the progress bar while ensuring it correctly reflects the element where iteration stopped.

                     Parameters

                            o exc_type (type[BaseException])

                            o exc_value (None | BaseException)

                            o traceback (None | TracebackType)

                     Return type
                            None

       class Timeout
              Utility for keeping track of time and raising an exception if a timeout is reached:

                 with Timeout(30) as timeout:
                     do_some_operation_async()

                     while operation_not_complete()
                         time.sleep(1)
                         timeout.tick()

                     return operation_result()

              __init__(timeout_secs: float = float('inf')) -> None

                     Parameters
                            timeout_secs (float) - the timeout.

                     Return type
                            None

              tick() -> None
                     Raises a TimeoutError if the time spent in the current context is greater than the timeout.

                     Return type
                            None

              __enter__() -> Self
                     Begins counting time and returns self.

                     Return type
                            Self

              __exit__(exc_type: type[BaseException], exc_value: None | BaseException, traceback: None | TracebackType) -> None
                     Resets the time counter.

                     Parameters

                            o exc_type (type[BaseException])

                            o exc_value (None | BaseException)

                            o traceback (None | TracebackType)

                     Return type
                            None

       download_file_using_browser(download_cmd: Callable[[], Any], file_extension: str, downloads_dir: str, timeout_secs: float = float('inf')) -> str
              Handles downloading a file with an external tool which should download it to some directory. Watches that directory and waits for the downloaded file to
              appear.

              Parameters

                     o download_cmd (Callable[[], Any]) - function which should trigger an asynchronous file download.

                     o file_extension (str) - the extension the downloaded file should have.

                     o downloads_dir (str) - path to where the file is expected to be downloaded.

                     o timeout_secs (float) - timeout for the download.

              Return type
                     str

       slugify(s: str) -> str
              Removes special characters from a string to turn it into a valid filename.

              Parameters
                     s (str) - string to convert.

              Return type
                     str

       import_file(file: str) -> ModuleType
              Dynamically imports a file based on a path, and returns the module object.

              Parameters
                     file (str) - path of the file to import.

              Return type
                     ModuleType

       tree(dir_path: str, prefix: str = '', stats: None | Callable[[str], str] = None) -> Iterable[str]
              Iterate over lines which together represent a directory tree in a pretty, human-readable format.

              Parameters

                     o dir_path (str) - path to the directory whose subtree we're interested in.

                     o prefix (str) - string that will appear at the start of each line.

                     o stats (None | Callable[[str], str]) - function which receives a file path and returns a string with information to display next to that file.

              Return type
                     Iterable[str]

       tabulate(records: list[list[str]], fillchar: str = ' ', use_color: bool = True, header_color: str = '', fill_color: str = colorama.Fore.BLACK + col-
       orama.Style.BRIGHT, column_colors: None | list[str] = None) -> Iterable[str]
              Turn a table into a nice printable format and iterate over its lines.

              Parameters

                     o records (list[list[str]]) - the table as a list of lists, where the outer lists are rows and the inner lists are columns.

                     o fillchar (str) - character to use as spacing between columns.

                     o use_color (bool) - whether columns should be colored for enhanced readability.

                     o header_color (str) - additional color to apply on the header row. It may combine with the column colors.

                     o fill_color (str) - color to use for fillchar.

                     o column_colors  (None | list[str]) - list of colors to use for columns. They will be used round-robin. If this is None, a default list of colors
                       will be used.

              Return type
                     Iterable[str]

       class TruncationStyle
              Enumeration of possible ways to truncate a string.

              NO_TRIM = 1
                     "What's in the box?" -> "What's in the box?"

              TRIM_END = 2
                     "What's in the box?" -> "What's i..."

              TRIM_START = 3
                     "What's in the box?" -> "...the box?"

              TRIM_MIDDLE = 4
                     "What's in the box?" -> "What...box?"

       truncate(s: str, max_len: int, ellipsis: str = '...', truncation_style: TruncationStyle = TruncationStyle.TRIM_END) -> str
              Truncates a string.

              Parameters

                     o s (str) - the string to truncate.

                     o max_len (int) - the maximum length beyond which the string will be truncated down to this length.

                     o ellipsis (str) - short string to use to indicate a truncated part of the string.

                     o truncation_style (TruncationStyle) - preference for which part of the string to truncate.

              Return type
                     str

       num_pretty(num: int, abbreviate: bool = True) -> str
              Formats a large number as a human-readable string.

              Parameters

                     o num (int) - the number to convert.

                     o abbreviate (bool) - whether to shorten large numbers with a magnitude sign.

              Return type
                     str

       parse_num_pretty(num_str: str) -> int
              Inverse of num_pretty().

              Parameters
                     num_str (str)

              Return type
                     int

       stable_dedup(elements: Iterable[TElem], key: None | Callable[[TElem], TKey] = None) -> Iterable[TElem]
              Removes duplicate elements from an iterable but preserves the original order.

              Parameters

                     o elements (Iterable[TElem]) - ordered collection of objects to deduplicate

                     o key (None | Callable[[TElem], TKey]) - function which receives an element and returns a value by which to deduplicate it.  I.e.,  all  elements
                       with the same key are considered duplicates.

              Return type
                     Iterable[TElem]

       str2bool(s: str) -> bool
              Parse some common, case-insensitive string representations of a boolean (`true', `yes', `no', `n', etc.).

              Parameters
                     s (str)

              Return type
                     bool

       move_clobber(src: str, dst: str, must_exist: bool = True) -> None
              Rename a file without caring if a different file by that name already exists.

              Parameters

                     o src (str)

                     o dst (str)

                     o must_exist (bool)

              Return type
                     None

       do_with_retries(action: Callable[[], T], num_retries: int = 10, sleep_between_retries: float = 1.0) -> T
              Invoke some function until it either succeeds or all retries are spent.

              Parameters

                     o action (Callable[[], T])

                     o num_retries (int)

                     o sleep_between_retries (float)

              Return type
                     T

   flam.attrutils
       This page documents the flam.attrutils module. It contains utilities for implementing custom attributes with minimal boilerplate.

       class EasyAttributeParams
              Parameters used to create an EasyAttribute.

              name_without_type: str

              aliases_without_type: list[str]

              findable_type: FindableType

              type_handler: TypeHandler

              is_ascending: bool

              truncation_style: TruncationStyle
                     Indicates how this attribute's values should be truncated if their string representation is too long.

              default_max_len: int
                     The maximum length beyond which attribute values as strings get truncated.

              __init__(name_without_type: str, aliases_without_type: list[str], findable_type: FindableType, type_handler: TypeHandler, is_ascending: bool, trunca-
              tion_style: TruncationStyle, default_max_len: int) -> None

                     Parameters

                            o name_without_type (str)

                            o aliases_without_type (list[str])

                            o findable_type (FindableType)

                            o type_handler (TypeHandler)

                            o is_ascending (bool)

                            o truncation_style (TruncationStyle)

                            o default_max_len (int)

                     Return type
                            None

       class EasyAttribute
              Utility for easily implementing an attribute. You always have the option of inheriting directly from Attribute, but if you inherit from this instead you
              will have to do a lot less work. You only need to implement an extractor function for getting the attribute value:

                 class MovieTitleAttribute(EasyAttribute):
                     def _extract_from_movie(self, movie: Movie, mlf_movie: MLFMovie) -> None | str:
                         return mlf_movie.title

                 register(MovieTitleAttribute(EasyAttributeParams(
                     name_without_type = 'title',
                     aliases_without_type = ['name', 'movie'],
                     findable_type = FindableType.MOVIES,
                     type_handler = STR_HANDLER,
                     is_ascending = True,
                     truncation_style = TruncationStyle.TRIM_END,
                     default_max_len = 45,
                 )))

              __init__(params: EasyAttributeParams) -> None

                     Parameters
                            params (EasyAttributeParams) - dataclass with all this attribute's parameters.

                     Return type
                            None

              str_of_value(value: AttributeValue, abbreviate: bool = False, **extras: Any) -> str
                     See Attribute.str_of_value. EasyAttributes support two optional extras:

                     o max_len - the maximum length beyond which the string will be truncated down to this length.

                     o ellipsis - short string to use to indicate a truncated part of the string.

                     Parameters

                            o value (AttributeValue)

                            o abbreviate (bool)

                            o extras (Any)

                     Return type
                            str

       type MovieExtractor = Callable[[EasyAttribute, Movie, MLFMovie], T]
              Type signature for _extract_from_movie.

       type PeopleExtractor = Callable[[EasyAttribute, People, list[MLFPerson]], T]
              Type signature for _extract_from_people.

       type RoleExtractor = Callable[[EasyAttribute, Role, MLFRolesDict, MLFMovie, list[MLFPerson]], T]
              Type signature for _extract_from_role.

       type Extractor = MovieExtractor | PeopleExtractor | RoleExtractor
              Type signature for any extractor method.

       easy_attribute(params: EasyAttributeParams) -> Callable[[Extractor[TRet]], EasyAttribute]
              Decorator  you  can  apply  to an extractor function to create an EasyAttribute from it and return an instance of that class.  This is an extra layer of
              convenience so you don't even have to explicitly inherit from EasyAttribute or instantiate it:

                 @register
                 @easy_attribute(EasyAttributeParams(
                     name_without_type = 'title',
                     aliases_without_type = ['name', 'movie'],
                     findable_type = FindableType.MOVIES,
                     type_handler = STR_HANDLER,
                     is_ascending = True,
                     truncation_style = TruncationStyle.TRIM_END,
                     default_max_len = 45,
                 ))
                 def _movie_title_extractor(self, movie: Movie, mlf_movie: MLFMovie) -> None | str:
                     return mlf_movie.title

              Parameters
                     params (EasyAttributeParams) - dataclass with all the attribute's parameters.

              Return type
                     Callable[[Extractor[TRet]], EasyAttribute]

       class TypeHandler
              Utility for operations pertaining to the type of an attribute's values, so you don't have to repeat the same code for each attribute of that type.

              Note that all attributes are assumed to also possibly return None or a list of values.  So even if your attribute returns list[None  |  float],  it  may
              still use FLOAT_HANDLER, for example.

              abstract property default_op: ComparisonOp
                     The default comparison operator that makes sense for this type.

              abstractmethod parse(primitive_str: str) -> AttributePrimitive
                     Parse a string representing a single primitive of this handler's type. You do not need to handle None or lists in this function.

                     Parameters
                            primitive_str (str) - a string representation of a single primitive value.

                     Return type
                            AttributePrimitive

              abstractmethod str_of(primitive: AttributePrimitive, abbreviate: bool, extras: dict[str, Any]) -> str
                     Return a string representation of a single primitive value of this handler's type. You do not need to handle None or lists in this function.

                     If abbreviate is false, then this method must be the inverse of parse().

                     Parameters

                            o primitive (AttributePrimitive) - a single primitive value. You may assume this is not None.

                            o abbreviate  (bool) - indicates if the string should be abbreviated. Abbreviation can mean different things for different types. It's al-
                              lowed to be lossy.

                            o extras (dict[str, Any]) - additional optional arguments to control the string conversion.

                     Return type
                            str

       class IntHandler
              Type handler for integers with support for pretty strings.

       class FloatHandler
              Type handler for floats with support for rounding off small decimal digits.

       class BoolHandler
              Type handler for booleans with support for various, case-insensitive ways you might describe them (`true', `yes', `no', `n', etc.).

       class StrHandler
              Type handler for strings.

       class MinutesHandler
              Type handler for minutes with support for pretty formatting of hours:minutes.

       class DateHandler
              Type handler for dates in many possible formats.

              property name: str
                     Name of the date format which can be appended to an attribute name to create a name for that  attribute  with  that  date  format.  Ex:  `-year',
                     `-day-of-month'.

              property datefmt: str
                     Format string for this date handler. See datetime.strftime in the python docs for format syntax.

              property is_ascending: bool
                     Suggestion for whether you should sort this attribute in ascending or descending order.

              strip(date: date) -> date
                     Takes a date and "zeroes out" the parts of the date which are irrelevant to this handler's datefmt.

                     For  instance,  a handler which returns the year only doesn't care about the day and month.  So by stripping those out, dates which have the same
                     year but different days and months can be considered equal.

                     Parameters
                            date (date)

                     Return type
                            date

       SMALL_INT_HANDLER = <flam.attrutils.IntHandler object>
              Handler object you can use for attributes which return small integers that do not need to be abbreviated.

       BIG_INT_HANDLER = <flam.attrutils.IntHandler object>
              Handler object you can use for attributes which return big integers that sometimes need to be abbreviated.

       FLOAT_HANDLER = <flam.attrutils.FloatHandler object>
              Handler object you can use for attributes which return floats.

       BOOL_HANDLER = <flam.attrutils.BoolHandler object>
              Handler object you can use for attributes which return booleans.

       STR_HANDLER = <flam.attrutils.StrHandler object>
              Handler object you can use for attributes which return strings.

       MINUTES_HANDLER = <flam.attrutils.MinutesHandler object>
              Handler object you can use for attributes which return a minutes duration.

       DATE_HANDLERS
              List of date handlers in many date formats available to use.

       class ArrayLengthAttribute
              Attribute which returns the number of elements in another attribute's return value. For attributes which don't return a list, the length is 1:

                 register(ArrayLengthAttribute(my_attr))

              __init__(wrapped_attr: Attribute) -> None
                     Initializes this attribute with the name `num-<wrapped_attr.name_without_type>'.

                     Parameters
                            wrapped_attr (Attribute) - the attribute whose num of elements will be returned.

                     Return type
                            None

       class StringLengthAttribute
              Attribute which returns length of another attribute's return value as a string.

              __init__(wrapped_attr: Attribute) -> None
                     Initializes this attribute with the name `len-<wrapped_attr.name_without_type>'.

                     Parameters
                            wrapped_attr (Attribute) - the attribute whose string length will be returned.

                     Return type
                            None

       class AverageAttribute
              Attribute which returns the average of another attribute's value.

              o For movie attributes, this will be a people attribute with the average across all movies those people were in.

              o For people attributes, this will be a movie attribute with the average across all people in the movie with the same crew type.

                 register(AverageAttribute(my_attr))

                 for ct in CrewType:
                     register(AverageAttribute(my_attr, as_crew_type=ct))

              __init__(wrapped_attr: Attribute, as_crew_type: None | CrewType = None) -> None
                     Initializes this attribute with the name `avg-<wrapped_attr.name_without_type>'.

                     Parameters

                            o wrapped_attr (Attribute) - the attribute whose average will be returned.

                            o as_crew_type (None | CrewType) -

                              optional modifier for this attribute. If provided, the attribute's name will be `avg-<wrapped_attr.name_without_type>-as-<crew_type>':

                              o For movie attributes, this will be a people attribute with the average across all movies those people were in as this crew type.

                              o For people attributes, this will be a movie attribute with the average across all people in the movie with this crew type.

                     Return type
                            None

       class SumAttribute
              Attribute which returns the sum of another attribute's value.

              o For movie attributes, this will be a people attribute with the sum across all movies those people were in.

              o For people attributes, this will be a movie attribute with the sum across all people in the movie with the same crew type.

              __init__(wrapped_attr: Attribute, type_handler: TypeHandler, as_crew_type: None | CrewType = None) -> None
                     Initializes this attribute with the name `sum-<wrapped_attr.name_without_type>'.

                     Parameters

                            o wrapped_attr (Attribute) - the attribute whose sum will be returned.

                            o as_crew_type (None | CrewType) -

                              optional modifier for this attribute. If provided, the attribute's name will be `sum-<wrapped_attr.name_without_type>-as-<crew_type>':

                              o For movie attributes, this will be a people attribute with the sum across all movies those people were in as this crew type.

                              o For people attributes, this will be a movie attribute with the sum across all people in the movie with this crew type.

                            o type_handler (TypeHandler)

                     Return type
                            None

EXTENDING FLAM
       You can extend flam with custom attributes, predicates, and fetchers.

       TIP:
          The repository includes an example file demonstrating how to implement custom extensions.

   Registering an extension
       To use an extension, you must register it. There are two ways to register an extension:

       1. Global extension
             Use register(), and the extension will be automatically available from any FlamContext.

                 @register
                 class MyCustomPredicate(Predicate,
                         name_without_type='is-movie-dope',
                         findable_type=FindableType.MOVIES):
                     # ...

             It's allowed to name your extension the same as an existing builtin. The builtin will be shadowed.

       2. Context extension
             Use FlamContext.register(), and the extension will be available to use only from that specific context.

                 class MyCustomPredicate(Predicate,
                         name_without_type='is-movie-dope',
                         findable_type=FindableType.MOVIES):
                     # ...

                 ctx = FlamContext()
                 ctx.register(MyCustomPredicate)

             It's allowed to name your extension the same as an existing builtin or global extension. They will be shadowed.

       Once registered, using an extension is just like using any builtin:

          filter = ctx.compile_movies_filter(['-is-movie-dope'])

       NOTE:
          For predicates and fetchers, you need to register the class itself. For attributes, you must register an instance of the class!

              # DO:
              ctx.register(MyCustomPredicate)
              ctx.register(MyCustomFetcher)
              ctx.register(MyCustomAttribute())

              # DON'T:
              ctx.register(MyCustomPredicate())
              ctx.register(MyCustomFetcher())
              ctx.register(MyCustomAttribute)

   Importing extensions automatically
       You can configure flam to automatically import your extensions module:

          flam config extension my_extensions.py

       The file must register its extensions globally. Now they will be automatically available from any FlamContext created with import_extensions=True.

   Implementing a custom attribute
       Attributes are implemented by inheriting from Attribute and implementing all its abstract members.

       However, there is an easier way. The flam.attrutils module provides a suite of utilities to help you implement attributes easily. You only need to provide:

       o Your attribute's parameters

       o A TypeHandler which corresponds to your attribute's return value (we probably already have the one you need)

       o An "extractor" function, which returns your attribute's value from the "MLF" (Movie List File) objects. These objects contain the  all  the  raw  fetch  data
         about your findable.

          @register
          @easy_attribute(EasyAttributeParams(
              name_without_type = 'title',
              aliases_without_type = ['name', 'movie'],
              findable_type = FindableType.MOVIES,
              type_handler = STR_HANDLER,
              is_ascending = True,
              truncation_style = TruncationStyle.TRIM_END,
              default_max_len = 45,
          ))
          def movie_title_extractor(self, movie: Movie, mlf_movie: MLFMovie) -> None | str:
              return mlf_movie.title

       The extractor function should have a different signature based on the findable type:

          # Extractor for movie attributes.
          def movie_title_extractor(self, movie: Movie, mlf_movie: MLFMovie) -> None | str:
              return mlf_movie.title

          # Extractor for people attributes.
          # mlf_people is sorted by uid. People attributes should return the attribute for each person a list with the same order.
          def people_name_extractor(self, people: People, mlf_people: list[MLFPerson]) -> list[None | str]:
              return [mlf_person.name for mlf_person in mlf_people]

          # Extractor for role attributes.
          def role_characters_extractor(self, role: Role, mlf_roles: MLFRolesDict, mlf_movie: MLFMovie, mlf_people: list[MLFPerson]) -> list[str]:
              return [
                  char
                  for mlf_person in mlf_people
                  for ct, mlf_role in mlf_roles[mlf_person.uid].items()
                  for char in mlf_role.characters
              ]

       NOTE:

          o Flam has special handling for attributes with return a list

          o Flam has special handling for attributes which return None. MLF data can often be None, so most attributes should be prepared to return it

          o The MLF objects you get are readonly! Don't modify them

          o Always create a copy if your attribute returns a mutable internal object:

                   def _movie_genres_extractor(self, movie: Movie, mlf_movie: MLFMovie) -> list[str]:
                       # GOOD: caller gets a copy of the list.
                       return list(mlf_movie.genres)

                       # BAD: caller gets the same list and might accidentally modify the MLF, which is not allowed.
                       return mlf_movie.genres

   Implementing a custom predicate
       Predicates are implemented by inheriting from Predicate. You'll need to:

       o Fill in some parameters like the predicate's name and type in the class declaration

       o Implement eat(), a classmethod for parsing your predicate

       o Implement some excrete function, for checking if the predicate holds true. The exact signature depends on the predicate's findable type:

                # Excrete for general predicates (predicates without a specific findable type).
                # General predicates also have the choice of implement all 3 of the type-specific excretes instead.
                def excrete(self, findable: Findable) -> bool:

                # Excrete for movie predicates.
                def _excrete_from_movie(self, movie: Movie, mlf_movie: MLFMovie) -> bool:

                # Excrete for people predicates.
                def _excrete_from_people(self, people: People, mlf_people: list[MLFPerson]) -> bool:

                # Excrete for role predicates.
                def _excrete_from_role(self, role: Role, mlf_roles: MLFRolesDict, mlf_movie: MLFMovie, mlf_people: list[MLFPerson]) -> bool:

       o Optionally implement regurgitate(), for decompiling your predicate

       o Optionally implement colonoscopy(), for inspecting subfilters in your predicate

       Here's what it all looks like:

          # Movie predicate which takes a list of CTGMs and a subfilter for roles, and checks if any role with any of those CTGMs passes the subfilter.
          @register
          class AnyRolePredicate(Predicate, name_without_type='any-role', findable_type=FindableType.MOVIES):
              def __init__(self, ct_gms: list[tuple[CrewType, GroupMode]], filter: Filter) -> None:
                  self._ct_gms = ct_gms
                  self._filter = filter

              @classmethod
              def eat(cls, params: EatParams, at: int) -> tuple[Predicate, int]:
                  # Functions which "eat" actually read a few tokens from index `at` and parse them into a result.
                  # They return the result and which index they stopped parsing at.
                  # We have a number of handy "eat" utils from our parent class.
                  ct_gms, filter_idx = cls.eat_listof(cls.eat_ct_gm, params, at)
                  sub_params = dataclasses.replace(params, find=FindableType.ROLES)
                  filter, until = cls.eat_subfilter(sub_params, filter_idx)
                  return cls(ct_gms, filter), until

              def _excrete_from_movie(self, movie: Movie, mlf_movie: _mlf.MLFMovie) -> bool:
                  for ct_gm in self._ct_gms:
                      for role in movie.associated_roles(*ct_gm):
                          if self._filter.excrete(role):
                              return True

                  return False

              def regurgitate(self) -> typing.Iterable[str]:
                  # Use `min` because LPAREN, RPAREN are sets of accepted strings and we want to pick one.
                  yield from super().regurgitate()
                  yield min(Pipeline.LPAREN)
                  yield from (ct_gm_to_str(*ct_gm) for ct_gm in self._ct_gms)
                  yield min(Pipeline.RPAREN)
                  yield from self._filter.regurgitate()

              def colonoscopy(self) -> typing.Iterable[FilterMember]:
                  yield self
                  yield from self._filter.colonoscopy()

   Implementing a custom fetcher
       Fetchers are implemented by inheriting from Fetcher. You'll need to:

       o Fill in some parameters like the fetcher's name in the class declaration

       o Implement _fetch_into_file()

          @register
          class MyCustomFetcher(Fetcher, list_type='my-database-list'):
              def fetch_into_file(self, movie_list_file: MovieListFile) -> None:
                  # ...

       Fetchers require gentle care in their implementation:

       o Familiarize yourself with the structure of MovieListFile

       o Remember to delete movies in the file that were removed from the list, and to avoid re-fetching movies already in the file

       o When deleting a movie, you don't need to delete its people. That's handled automatically

       o Don't  add  movies  to  the  file until you've acquired all of their data, so that if fetch is interrupted at any point, the file will be in a good, saveable
         state

       o If your fetcher is slow, call _checkpoint() from time to time so that a crash won't cause the data to be lost

       o Be sure to handle server errors from the API you're fetching from, and raise FetchInterrupt as needed

       o It's also nice to handle KeyboardInterrupt by raising FetchInterrupt

       See it all in action:

          # This fetcher takes a size of a list to "fetch" and literally makes up a list with phony data.
          @register
          class RandomDataFetcher(Fetcher, list_type='random-size'):
              def _fetch_into_file(self, movie_list_file: MovieListFile) -> None:
                  # Takes the address to mean the number of movies the random list should have.
                  num_movies = int(self.concrete_listdef.address)

                  # We must assign a UID for each movie. Usually this would be, like, the IMDb ID of that movie.
                  # For this example we'll just use the index as the uid.
                  movie_uids_in_list = [str(movie_idx) for movie_idx in range(num_movies)]

                  # Remove all movies that were previously fetched but are no longer part of the list.
                  movie_list_file.movies_by_uid = {uid: m for uid, m in movie_list_file.movies_by_uid.items() if uid in movie_uids_in_list}

                  # Now add all the movies that aren't already in the list from a previous fetch.
                  # Use this progressbar utility to print our progress to stdout as we go.
                  with utils.ProgressBar([uid for uid in movie_uids_in_list if uid not in movie_list_file.movies_by_uid],
                          desc='Downloading',
                          keyfunc=lambda uid: uid) as bar:
                      for uid in bar:
                          try:
                              self.fetch_movie(movie_list_file, uid)
                          except KeyboardInterrupt as e:
                              raise FetchInterrupt("User interrupted fetch in the middle.") from e

                          # Save what we've fetched so far, so that if we experience a crash, data won't have to be re-fetched.
                          self._checkpoint(movie_list_file)

              def fetch_movie(self, movie_list_file: MovieListFile, uid: str) -> None:
                  TITLES_POOL = ['Star Wars', 'Inside Llewyn Davis', 'Lord of the Rings', 'Interstellar']
                  CHARACTERS_POOL = ['Darth Vader', 'Llewyn Davis', 'Galadriel', 'Murph']

                  # We'll seed the movie's RNG using its uid.
                  rng = random.Random(uid)

                  # It's important to acquire all the movie's data and populate the file with the people from the movie before we add the movie itself.
                  # So we'll start by building the movie's crews.
                  crew = {}

                  for crew_type in CrewType.iterate_except_any():
                      # Randomly decide how many people are in this movie in this crew type.
                      # For their uid, we'll add some random base so that you don't get the same people everywhere.
                      num_crewmembers = rng.randint(0, 10)
                      base_uid = rng.randint(0, 1000)

                      crew[crew_type] = MLFCrew(
                          crew_type = crew_type,
                          roles_by_uid = {},
                      )

                      for person_idx in range(num_crewmembers):
                          # Make up some uid for this fake person.
                          person_uid = str(base_uid + person_idx)

                          # For actors, we'll make up a bit of data about which character they played.
                          # We can always fill in None, or leave lists empty if we're missing that data.
                          crew[crew_type].roles_by_uid[person_uid] = MLFRole(
                              person_uid          = person_uid,
                              is_star             = None,
                              episodes_num        = None,
                              oscar_noms          = [],
                              oscar_wins          = [],
                              characters          = [rng.choice(CHARACTERS_POOL)] if crew_type == CrewType.CAST else [],
                              jobs                = [],
                          )

                          # This person may have already been fetched because of their presence in another movie or another crew type in this movie.
                          if person_uid not in movie_list_file.people_by_uid:
                              self.fetch_person(movie_list_file, person_uid)

                  # We support some data about movies which is actually specifically about that movie's presence in this list.
                  # For example, the date it was added to this list.
                  per_src_data = MLFMoviePerSourceData(
                      canon_listdef       = movie_list_file.abstract_listdef,
                      list_index          = int(uid),
                      list_note           = None,
                      listing_date        = None,
                  )

                  mlf_movie = MLFMovie(
                      uid                 = uid,
                      per_src_data        = [per_src_data],
                      media_type          = 'movie',
                      title               = rng.choice(TITLES_POOL),
                      original_title      = None,
                      tagline             = None,
                      synopsis            = None,
                      url                 = None,
                      runtime_minutes     = 145,
                      metascore_votes     = None,
                      metascore           = 82,
                      votes               = None,
                      rating              = 8.5,
                      my_rating           = None,
                      likes               = None,
                      is_liked            = None,
                      budget_usd          = None,
                      revenue_usd         = None,
                      content_rating      = None,
                      release_date        = datetime.date(1969, 7, 4),
                      watch_dates         = [],
                      my_notes            = [],
                      episodes_num        = None,
                      seasons_num         = None,
                      end_date            = None,
                      genres              = ['Drama', 'Western'],
                      studios             = ['Paramount Pictures', 'Rafran Cinematografica', 'San Marco'],
                      languages           = ['English', 'Italian', 'Spanish'],
                      countries           = ['Italy', 'United States'],
                      crew                = crew,
                  )

                  # Add the movie to the file only at the end, after all its data is acquired, and all the people from the movie are already added!
                  movie_list_file.movies_by_uid[mlf_movie.uid] = mlf_movie

              def fetch_person(self, movie_list_file: MovieListFile, uid: str) -> None:
                  NAMES_POOL = ['James', 'Spencer', 'Hayden', 'David', 'Oscar', 'Cate', 'Jessica', 'Ellen']
                  GENDERS_POOL = ['male', 'female', 'nonbinary']

                  # We'll seed the person's RNG using its uid.
                  rng = random.Random(uid)

                  movie_list_file.people_by_uid[uid] = MLFPerson(
                      uid                 = uid,
                      name                = rng.choice(NAMES_POOL),
                      gender              = rng.choice(GENDERS_POOL),
                      birthday            = datetime.date(1989, 2, 24),
                      deathday            = None,
                      death_reason        = None,
                      height_cm           = 174.0,
                      countries           = ['England'],
                  )

CHANGELOG
   2026.6.25
       o Fixed a bug where fetching IMDb lists no longer worked following IMDb site changes

       o Improved stability of letterboxd fetcher

   2026.6.7
       o Documentation enhancements

       o Fixed a bug with version upgrades

   2026.6.6
       o And awaaaay we go!

AUTHOR
       Aviv Edery

COPYRIGHT
       2026, Aviv Edery

2026.6.25                                                                    Jun 25, 2026                                                                  FILMFLAM(1)
