HOWTO: query data from PLEX on a Mac

Hope someone can shed some light onto this, as I am struggling with about half my movies not working for the poster method.



I want to be able to determine a few things about my collection via the database:

  1. List of films using thumbnails from the film (as opposed to a poster)
  2. List of films that have a grey “broken” poster… it seems sometimes(?) if you upload a poster for a film manually (by PMS) then rename and rescan that the poster is lost.



    My problem comes in that I can’t merely follow the path and confirm that Contents/_stored/posters exists because some films with poster don’t resolve correctly.



    For example:

    guid = com.plexapp.agents.imdb://tt0028302?lang=en

    user_thumb_url = file://localhost/Users/username/Downloads/lo-squadrone-bianco_fondo-magazine.jpeg



    after hashing the guid I get a5d919bbfe98f08677a298a3d2c5f9f0d3217aff which leads to /Users/username/Library/Application Support/Plex Media Server/Metadata/Movies/a/5d919bbfe98f08677a298a3d2c5f9f0d3217aff.bundle/Contents/_stored/posters/ … but that directory doesn’t exist. This was a film that I uploaded a cover for, and it correctly displays the poster in Plex + PMS so I am missing something here. The 5d919bbfe98f08677a298a3d2c5f9f0d3217aff.bundle/Contents dir exists but there is no _stored folder.



    Also, another film that is showing a thumbnail:

    guid = com.plexapp.agents.imdb://tt0166198?lang=en

    user_thumb_url = media://d/aeb9fcbf94993ed2cb3a2a12ac15e63214b58f0.bundle/Contents/Thumbnails/thumb1.jpg

    Hashing the guid yields 0e98ff5f2935b59ac447da610ddd0de78a57673

    So I would expect /Users/username/Library/Application Support/Plex Media Server/Metadata/Movies/0/3e98ff5f2935b59ac447da610ddd0de78a57673.bundle/Contents to have the thumb, but no luck.



    This method does work for some films though, so it’s not that I am not hashing properly, and those directories do exist, but it seems that Plex gets the thumbs and uploaded covers in a different manner from the normal scan.



    Any help is greatly appreciated.

Thank you OP!

*Sits down to write a complex join query to show/hide sections based on section id*

No parental controls in plex, chalking this one out as "for the kids" :D

Hmm, tried making views of the tables to populate kid friendly content and adult content.


Not as simple as I thought! Would need to change the view name to the table name where the GUI pulls data from, for each client and change the table name where the server stores stuff. Sheesh! I’m not giving up though! Public boolean value/column does not work.


One question, if anyone could advise. I am done adding sections, don’t plan to add anymore sections i.e. So I won’t screw up the seq.nextval of the sequence that generates section ID if I do the following?


The section ID of my adult content is 2, normal kid content is 1 and 3.


If I delete section ID row for 2, it hides the adult content from web GUI, will it show up in any other client besides iPad/iOS(Tested, does not show up)?


Later when the kids are asleep, I open up terminal and add that row I originally deleted, with the details originally generated to add section id 2 with details.


What repercussions should I be facing, changing stuff from the backend i.e.


I update the date added in one of the tables to hide adult content from the “recently added” section of web/gui client.


Where does “on deck” section pull data/store data in the tables!


Appreciate any help!


Just want to hide adult content from the kids and enjoy plex gui/multiple clients!


Cheers!

Fantastic topic!

I have a media library and have been using PMS recently, as the PlexConnect software allows me to view media from my library on my AppleTV 3rd Gen. Other devices use things such as XBMC to access media, and I have the library laid out in a nice folder structure. This is fine for XBMC, but PlexConnect won't see items in nested folders. Collections seem to be the best way to get things working without having to restructure my media items.

Alas there is no multi-edit, and adding collection info 20+ episodes of a show manually is a pain. This is where finding this topic cheered me up.

After reading, digesting, and playing around, I'm getting the hang of how things work in regards to collections and tags.

It seems that the tag itself is in the tags table (makes sense), you then need to add an entry to the taggings table for the media you wish to tag (it uses the "id" of the media as found in the metadata_items table), perhaps adding multiple entries if you wish the media to be part of multiple collections (I have shows split into the main show, then series, e.g.; a tag of Fringe, applied to all Fringe episodes, and a tag of "Fringe - Season 1' applied to just season 1 episodes)

When I'm done (just writing a couple of applescripts to speed things up) I'll post up useful SQL statements and possibly the scripts I've written which may help others. Knowing my luck, Plex will come out with a new version that supports multi-editing of collection information once I do this :D

** edit **

Here's the thread I created with the info I gleaned from this thread, plus my own delving into things: http://forums.plexapp.com/index.php/topic/77010-setting-up-tags-collections-for-multiple-tv-show-episodes-at-once/

Is there any reason why my database is 12 MB, but not displaying any data when queried? 

I tried both sqlite3 on my mac, and sqlitebrowser on the pc. The data base is located on the pc server. 

Thanks.

Cast & Crew + Show, Season & Episode info

Thank you very much SolarPlex for the brilliant thread. I just wanted to share my experience about pulling metadata from Plex SQLite db.

It seems that db design has improved over the past 2 years to accommodate multiple values joined by the '|' operator in metadata_items.tags_xxx fields, as in 'Black-and-white|Drama', my understanding is that for common tags such as director, country etc... tags and taggings tables are now used to store user manual input 'for some time', then on some occasion, these values are stored back into metadata_items.tags_xxx fields.

If this may be of some help, below is the query I use to populate an Excel catalog of movies :

[EDIT 12 FEB 2014] : the last field, i.hints holds TV Show specific data : show, season and episode

I use VBA code to expand the field onto 3 columns; feel free to PM me if you need more details.

SELECT 
  d.id AS id, MAX(p.id) AS part, p.media_item_id AS item,
  CASE WHEN ifnull(d.tags_country,'') <> '' THEN d.tags_country ELSE COALESCE((SELECT t.tag FROM taggings AS g JOIN tags AS t ON g.tag_id = t.id WHERE g.metadata_item_id = d.id AND t.tag_type = 8),'') END AS country,
  CASE WHEN ifnull(d.tags_director,'') <> '' THEN d.tags_director ELSE COALESCE((SELECT t.tag FROM taggings AS g JOIN tags AS t ON g.tag_id = t.id WHERE g.metadata_item_id = d.id AND t.tag_type = 4),'') END AS director,
  CASE WHEN ifnull(d.tags_star,'') <> '' THEN d.tags_star ELSE COALESCE((SELECT t.tag FROM taggings AS g JOIN tags AS t ON g.tag_id = t.id WHERE g.metadata_item_id = d.id AND t.tag_type = 6),'') END AS actor,
  CASE WHEN ifnull(d.tags_genre,'') <> '' THEN d.tags_genre ELSE COALESCE((SELECT t.tag FROM taggings AS g JOIN tags AS t ON g.tag_id = t.id WHERE g.metadata_item_id = d.id AND t.tag_type = 1),'') END AS genre,
  CASE WHEN ifnull(d.tags_collection,'') <> '' THEN d.tags_collection ELSE COALESCE((SELECT t.tag FROM taggings AS g JOIN tags AS t ON g.tag_id = t.id WHERE g.metadata_item_id = d.id AND t.tag_type = 2),'') END AS collection,
  d.title || CASE WHEN ifnull(d.original_title,'') = '' OR ifnull(d.original_title,'') = d.title THEN '' ELSE ' (' || d.original_title || ')' END AS title,
  d.[year], p.file,
  i.hints
FROM 
  metadata_items AS d, media_items AS i, media_parts AS p, library_sections AS l
WHERE 
  d.id = i.metadata_item_id AND i.id = p.media_item_id AND l.id = d.library_section_id AND 
  l.[name] = 'movies' AND d.deleted_at IS NULL
GROUP BY p.media_item_id;

Part II

The above is, of course, far from complete. One thing I have not mentioned so far is how "cast & crew" information is handled. The database layout is similar to what we see on the Internet Movie Database web site. The fields "tags_director", "tags_writer" and "tags_star" appear on top of the page and are stored in the "metadata_items" table in PLEX, but if you dig deeper IMDB also has an extra "Cast & Crew" information that contains a more detailed list of all the people involved in making a particular movie - partly repeating the same information.

PLEX does exactly the same, it uses an extra table for this type of information and it may be a little tricky to understand how it works.

Three tables are involved:

  • 'metadata_items':
    This is the table we already know with all the general info on a movie, where each movie has exactly one 'id'
  • 'tags':
    Think of the table "tags" as a people database for a moment. It contains the name of a person or category as 'text', the function of a person or category as 'tag_type'. Each person or category with one specific function has exactly one 'id' here. Some people can appear twice or even thrice with different IDs because they may appear in movies aswriters, actors and directors. Clint Eastwood would be a prominent example. Clint Eastwood as director would have tag_type=4, the actor would be tag_type=6,
    the writer would be tag_type=5
    [*]'taggings':
    this one brings movies and people together. Whenever Clint Eastwood appears in a movie and entry in taggings is generated (or should be) that consists of the ID from metadata_items (metadata_items_id), the ID from tags (tag_id) an optional text (text) that may contain the role he plays in that movie, and an index that gives the position in the cast & crew list.

table "taggings"
----------------------------------------------------
text                         = character played in movie
tag_id                       = id from table "tags"
metadata_item_id             = id from table "metadata_items"
index                        = Position in list	

table “tags”

tag = name of person
tag_type = 1=genre,2=collection,4=director,5=writer,6=actor,8=country
id = tag_id from table “taggings”


Complicated? Well, not really, is it? What is the benefit of all this, why do we need this cast & crew list when we already have tags_director, tags_star and tags_writer? The reason is that the people’s tags in “metadata_items” are limited: There is just one single row for each movie, but there may be more than one piece of information, i.e. multiple actors, multiple writers, multiple genres, etc. A database, however, stores one piece of information once in one place and refer to that same information from all other places. Actually, the information about actors, directors, writers, genres, collection in our table metadata_items are derived from other tables to speed up some often needed queries where we do not need the complete information.


If we want to know all movies that have Angelina Jolie in it, we can search all of the tags_star fields in the table metadata_items for that string (as discussed above). But the proper and more reliable way is to look up tag=“Angelina Jolie” where tag_type=6 in the table “tags” and feed the found ids to the table “taggings” that returns the ids in the metadata_items. We could do it like this (clumsy):

SELECT title FROM metadata_items JOIN taggings JOIN tags WHERE tags.tag=“Angelina Jolie” AND tags.tag_type=6 AND tags.id=taggings.tag_id AND taggings.metadata_item_id=metadata_items.id;

Or much more elegantly with nested SELECT commands and the keyword IN that you may already know from shell scripts, C, Python or whatever:

SELECT title FROM metadata_items WHERE id IN (SELECT metadata_item_id FROM taggings WHERE tag_id IN (SELECT id FROM tags WHERE tag=“Angelina Jolie”));

“IN” is pretty cool. You can use it very often with nested commands where you pass on results from one table to query a second one.


That was the database READING perspective.

This is a very helpful post; thank you.

Anyone here a fan of VoxCommando and managed to leverage this access to the Plex DB to use VC w/ Plex??

Here is a new issue of Things you had been told not to do but could not resist trying out anyway for the automatomaniacs among you.

Today I bowed over the question what I could do with my movie ratings. They are all from TMDB and they tend to be somewhat extreme. Actually, I rather trust the ratings found on IMDB. So I wondered whether I could replace all TMDB ratings by IMDB ratings automatically while keeping everything else unchanged. It was easier than I had thought, a few lines of BASH script did the trick:
 

#! /bin/bash

Database path

DATABASE=$HOME/Library/Application\ Support/Plex\ Media\ Server/Plug-in\ Support/
Databases/com.plexapp.plugins.library.db

Library Section ID to process

LSID=1

Make a list (array) with all database items that have an IMDB-based guid

ID_LIST=($(sqlite3 “$DATABASE” “SELECT id FROM metadata_items WHERE guid LIKE ‘%imdb://tt%’ AND library_section_id IN ( $LSID )”))

Just a counter

I=1

clear

Loop through all elements of the list

for ID in ${ID_LIST[@]} ; do
# Extract the relevant part from the guid that contains the “tt12345667”
IMDB_ID=$(echo “SELECT substr(replace(guid,‘com.plexapp.agents.imdb://’,‘’),1,9) FROM metadata_items WHERE id=$ID ;” | sqlite3 “$DATABASE”)

# Retrieve info from imdbapi.com using curl and then extract only the rating using sed's picket fences
IMDB_RATING=$(curl -s -f http://www.imdbapi.com/?i=$IMDB_ID | sed -e 's/^.*Rating":"//' -e 's/\(.\{3\}\).*/\1/') 

# If a rating could be retrieved write it to the database
if [  "$IMDB_RATING" != "" ] ; then
  
  # This is the dangerous stuff
  echo "UPDATE metadata_items SET rating=\"$IMDB_RATING\" WHERE id=$ID ;" | sqlite3 "$DATABASE"   

  # Let the user know that we are actually doing something
  tput cup 0 0
  echo "$I of ${#ID_LIST[@]} processed, updated rating of movie $ID to $IMDB_RATING                     "
fi

# increase counter, duh...
I=$(($I+1))

done

What I did here was first ask the database how many of my movies had a guid value based on an IMDB id (the tt12345677 stuff). Most of them. Fine. That meant that I could easily extract the IMDB id from the guid value. So I looped through all matching movies, extracted their IMDB id, then requested the IMDB rating for that id from the website www.imdbapi.com and finally updated the values of "rating" in metadata_items accordingly. Not complicated actually, the script went through all ~ 1000 movies without errors and within about 20 minutes all TMDB ratings were replaced by IMDB ratings.

Same warning as always: Make a backup before you try out stuff like that.

Cheers
Smil

I'm trying to do this on ubuntu but not getting it to work (Yes this is the mac forum, but bash is bash)

I can verify that

  • Database path = OK
  • SELECT statements = OK
  • Retrieve info from imdbapi.com = OK

For testing purpose, I'm selecting a single film in a copy of the plex db.

#! /bin/bash

Database path

DATABASE=$HOME/Documents/Movieadmin/com.plexapp.plugins.library.db

Make a list (array) with all database items that have an IMDB-based guid

ID_LIST=$(sqlite3 “$DATABASE” “SELECT * FROM metadata_items WHERE guid = “com.plexapp.agents.imdb://tt0435623?lang=en””)

Just a counter

I=1

clear

Loop through all elements of the list

for ID in $ID_LIST ; do
    # Extract the relevant part from the guid that contains the “tt12345667”
    IMDB_ID=$(echo “SELECT substr(replace(guid,‘com.plexapp.agents.imdb://’,‘’),1,9) FROM metadata_items WHERE id=$ID ;” | sqlite3 “$DATABASE”)
    
    # Retrieve info from imdbapi.com using curl and then extract only the rating using sed’s picket fences
    IMDB_RATING=$(curl -s -f http://www.imdbapi.com/?i=$IMDB_ID | sed -e ‘s/^.Rating":"//’ -e 's/(.{3})./\1/’)
    
    # If a rating could be retrieved write it to the database
    if [  “$IMDB_RATING” != “” ] ; then
      
      # This is the dangerous stuff
      echo “UPDATE metadata_items SET rating="$IMDB_RATING" WHERE id=$ID ;” | sqlite3 “$DATABASE”   

      # Let the user know that we are actually doing something
      tput cup 0 0
      echo “$I of ${#ID_LIST[@]} processed, updated rating of movie $ID to $IMDB_RATING”
    fi
    
    # increase counter, duh…
    I=$(($I+1))
done

The error message is:

"Error: near ".": syntax error"

I'm just not able to find out where the problem is, anyone??

NB! I changed the "for ID in ${ID_LIST[@]} ; do" to "for ID in $ID_LIST ; do" because it gave me an "Bad substitution" error.

Awesome tutorial… Just what I’m looking for, for my next step in automation…


Having an initial issue though working through the first bit…

.mode csv

.output $HOME/Desktop/mymovies.csv

SELECT title,year,tags_genre,tags_director,tags_rating,tags_collection FROM metadata_items WHERE library_section_id=1;

Pit doesn’t want to output the file… Says unable to write file when I try run it…


Eventually what I’m hoping for is to be able to automate the process, with Automator maybe?, and have a cvs file produced with all my movies and tv shows list in it looking like this maybe

Movies, Iron Man, 2008, Robert Downy Jr Gweneth Paltrow, 3000mb

TV, Big Bang Theory, 2007, S01E01, Pilot, Kaley Cuoco Jim Parsons, 700mb


And then be able to provide someone (aka family members who are to lazy to download their own stuff or not sure how lol) with that file and have them delete the lines for items they don’t want, and put it on their hdd… Then I’ll just have to come home and plug that into my media center and it’ll automatically copy what’s listed in the file to their hdd following my directory structure…

Movies/Title [Year]/title [Year] [1080p].ext

TV/Show Title [Year]/Season 1/Show Title - S01E01 - Pilot [1080p].ext


I’ve started this venture cause I find I’m always asked what I have, and I reply what you want cause I probably have it, and they always say jut pick stuff we’ll like… And all having smaller storages than mine or my total media collection they translates to large chunks of time filter through my collection and copying stuff I think they “may” like lol…

Thanks te5s3rak1 for bringing this thread to my attention. Missed it last time it came to life

Brilliant stuff.

I have been trying to find if i can run some scripts that would export every table to excel to allow me to use excel comparison tools i have to spot changes to help me investigate issues such as why every now and then Plex Media Server loses tons of thumbnails !

I will read the thread carefully and scrape the stuff out. I am also specifically interested in knowing the link from the metadata item to the cache phototranscoder files - have not been able to find that link to the cache file and also to find out if there is a one-to-many relation or only one cache file is stored at any one time or multiple sizes as they get requested. 

anyone have thoughts on my not being able to use

.mode csv
.output $HOME/Desktop/mymovies.csv
SELECT title,year,tags_genre,tags_director,tags_rating,tags_collection FROM metadata_items WHERE library_section_id=1;

to get a file output?

I find the XMLs from the web service are just way easier to use.

how I get them... I basically just need to get an output of my media server information (database), then I can run some statistical analysis on it from there... as well as setup my auto copy script somehow...

The XMLs are faster and already have the joined views you'd have to discover how to query in the dbase. As well as the web service is platform agnostic and doesn't require additional connections/shares...etc should u want to access remote

If your movies library is the 2nd one like mine

http://IP:32400/library/sections/2/all

otherwise replace your section number.

how I get them... I basically just need to get an output of my media server information (database), then I can run some statistical analysis on it from there... as well as setup my auto copy script somehow...

I like the idea of an SQL output though so I can run it through Automator…


Does anyone have an idea why it’s not letting me set my output file

Might also wanna take a peak here:

https://forums.plex.tv/topic/103115-rel-plex2csv/

/T

I'm trying to do this on ubuntu but not getting it to work (Yes this is the mac forum, but bash is bash)

I can verify that

  • Database path = OK
  • SELECT statements = OK
  • Retrieve info from imdbapi.com = OK

For testing purpose, I'm selecting a single film in a copy of the plex db.

#! /bin/bash

Database path

DATABASE=$HOME/Documents/Movieadmin/com.plexapp.plugins.library.db

Make a list (array) with all database items that have an IMDB-based guid

ID_LIST=$(sqlite3 “$DATABASE” “SELECT * FROM metadata_items WHERE guid = “com.plexapp.agents.imdb://tt0435623?lang=en””)

Just a counter

I=1

clear

Loop through all elements of the list

for ID in $ID_LIST ; do
    # Extract the relevant part from the guid that contains the “tt12345667”
    IMDB_ID=$(echo “SELECT substr(replace(guid,‘com.plexapp.agents.imdb://’,‘’),1,9) FROM metadata_items WHERE id=$ID ;” | sqlite3 “$DATABASE”)
    
    # Retrieve info from imdbapi.com using curl and then extract only the rating using sed’s picket fences
    IMDB_RATING=$(curl -s -f http://www.imdbapi.com/?i=$IMDB_ID | sed -e ‘s/^.Rating":"//’ -e 's/(.{3})./\1/’)
    
    # If a rating could be retrieved write it to the database
    if [  “$IMDB_RATING” != “” ] ; then
      
      # This is the dangerous stuff
      echo “UPDATE metadata_items SET rating="$IMDB_RATING" WHERE id=$ID ;” | sqlite3 “$DATABASE”   

      # Let the user know that we are actually doing something
      tput cup 0 0
      echo “$I of ${#ID_LIST[@]} processed, updated rating of movie $ID to $IMDB_RATING”
    fi
    
    # increase counter, duh…
    I=$(($I+1))
done

The error message is:

"Error: near ".": syntax error"

I'm just not able to find out where the problem is, anyone??

NB! I changed the "for ID in ${ID_LIST[@]} ; do" to "for ID in $ID_LIST ; do" because it gave me an "Bad substitution" error.

Today I finally got time to get SolarPlex's script to work on my Synology DS1813+. Since it uses Linux, it maybe can help you too.

Your error looks like a SQL error...although I don't see any dot (.) in your code that could through that error. Where in the code or at which SQL statement does it throw the error?

For me it was a wrong section numer which returned an empty array and then killed the script. You already removed the brackets which were giving me errors as well.

(Since http://www.imdbapi.com was shut down, http://www.omdbapi.com took it's place.)

#!/opt/bin/bash
#/opt/bin/bash /volume1/Plex/Library/Application\ Support/Plex\ Media\ Server/Plug-in\ Support/Databases/
#Database path

DATABASE=/volume1/Plex/Library/Application\ Support/Plex\ Media\ Server/Plug-in\ Support/Databases/com.plexapp.plugins.library.db

Library Section ID to process

LSID=21

counter added for partial runnings

COUNTER_ID=45638

Make a list (array) with all database items that have an IMDB-based guid

ID_LIST=$(sqlite3 “$DATABASE” “SELECT id FROM metadata_items WHERE id>$COUNTER_ID AND guid LIKE ‘%imdb://tt%’ AND library_section_id IN ($LSID)”)

Just a counter

I=1

#clear

Loop through all elements of the list

for ID in $ID_LIST[@]; do
    # Extract the relevant part from the guid that contains the “tt12345667”
    IMDB_ID=$(echo “SELECT substr(replace(guid,‘com.plexapp.agents.imdb://’,‘’),1,9) FROM metadata_items WHERE id=$ID ;” | sqlite3 “$DATABASE”)
    
    # Retrieve info from imdbapi.com using curl and then extract only the rating using sed’s picket fences
    IMDB_RATING=$(curl -s -f http://www.omdbapi.com/?i=$IMDB_ID | sed -e ‘s/^.Rating":"//’ -e 's/(.{3})./\1/’)
    
    # If a rating could be retrieved write it to the database
    if [  “$IMDB_RATING” != “” ] ; then
      
      # This is the dangerous stuff
      echo “UPDATE metadata_items SET rating="$IMDB_RATING" WHERE id=$ID ;” | sqlite3 “$DATABASE”   

      # Let the user know that we are actually doing something
      tput cup 0 0
      echo "$I of $#ID_LIST[@] processed, updated rating of movie $ID to $IMDB_RATING                     "
    fi
    
    # increase counter, duh…
    I=$(($I+1))
done

First of all a big Thank You! @SolarPlex for all the info, which is still extremely useful and pretty much exactly what I was looking for.
In the meantime a lot of stuff was added, as others pointed out, among other things some more tag_types.
Is there a table somewhere that explains, what these tag_types are?
200+ types seem to be photo related; 300+ music, but there are persons/names with tag_types 7 and 10 - what are these types?

I know it’s a fast hack did’t bother to make it good, but it works,

I wanted to copy all fanart and posters to the movie dir, all movies got it’s own directory. It’s in php because I’m not to good with shell programming. But if anyone want to try you are free to go. It’s only working with movies at the moment and I’m not sure if it works for anyone else. mi.library_section_id = “1” is my Movies dir in plex.

I copy the database to my home dir, change to your own home dir.

<?php

// change here to your own home dir
copy("/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db", "/home/magnus/com.plexapp.plugins.library.db" );

$db = new SQLite3('com.plexapp.plugins.library.db');

$movie_dir ="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Metadata/Movies/";

$results = $db->query('SELECT mi.title, mi.guid, mi.library_section_id, mi.user_thumb_url, mi.user_art_url, mp.file, mp.media_item_id FROM metadata_items AS mi, media_parts AS mp WHERE mi.library_section_id = "1" AND mi.id = mp.media_item_id');
while ($row = $results->fetchArray()) {
    $guid = sha1( $row['guid'] );
    $cache_dir = $guid[0];
    $item_sha1_dir = substr($guid, 1);

    $base = substr($row['file'],0, strripos($row['file'], "/"));
    $art_name = substr($row['user_art_url'] ,strripos($row['user_art_url'], "/") + 1);
    $thumb_name = substr($row['user_thumb_url'] ,strripos($row['user_thumb_url'], "/") + 1);

    $copy = $movie_dir . $cache_dir . "/" . $item_sha1_dir . ".bundle/Contents/_stored/";
    $copyart = $copy . "art/" . $art_name;
    $copyposter = $copy . "posters/" . $thumb_name;

    echo $row['title']. "
";
//    echo "Fanart: " . $copyart . "
";
//    echo "Poster: " . $copyposter . "
";

    copy( $copyart, $base . "/fanart.jpg" );
    copy( $copyposter, $base . "/poster.jpg" );
}
?>

I just recently started to pick apart my plex db, my motivation being that I want to learn about SQL/databases, and your plex sql cheat sheet is awesome!
Figuring out which tables I can join by which variables is really handy.