Skip to content

Instantly share code, notes, and snippets.

@mauvehed
Created August 24, 2015 04:14
Show Gist options
  • Save mauvehed/b2f6b2de6452ac63b865 to your computer and use it in GitHub Desktop.
Save mauvehed/b2f6b2de6452ac63b865 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
import sqlite3
import os
import urllib
import unicodedata
import getopt
import sys
import codecs
# Find unscanned files not in Plex database but on filesystem
# Version 0.03
# - 0.01 - Initial release
# - 0.02 - Go through all sections and verify DB exists and optionally write results to file
# - 0.03 - Add option to generate lists of matched files for each section (-l)
VIDEO_EXTENSIONS = ['.m4v', '.3gp', '.nsv', '.ts', '.ty', '.strm', '.rm', '.rmvb', '.m3u', '.mov', '.qt', '.divx', '.xvid', '.bivx', '.vob', '.nrg', '.img', '.iso', '.pva', '.wmv', '.asf', '.asx', '.ogm', '.m2v', '.avi', '.bin', '.dat', '.dvr-ms', '.mpg', '.mpeg', '.mp4', '.mkv', '.avc', '.vp3', '.svq3', '.nuv', '.viv', '.dv', '.fli', '.flv', '.rar', '.001', '.wpl', '.zip', '.jpg']
OK_FILES = ['imdb.nfo', '.DS_Store', '__db.001']
OTHER_EXTENSIONS = ['.srt', '.sub', '.idx', '.jpg']
def usage():
print "Usage: python %s [OPTION]" % (__file__)
print "Options:"
print "-f, --file <filename> Write out results to <filename>"
print "-h, --help This message"
print "-i, --ignores Display list of files that are within paths that aren't video extensions or subtitles"
print "-l, --list Create files listing all matched files in plex"
return
def listTree(top, files=list()):
r = files[:]
if not os.path.exists(top):
print 'Not mounted: ' + top
return r
for f in os.listdir(top):
pathname = os.path.join(top, f)
if os.path.isdir(pathname):
r = listTree(pathname, r)
elif os.path.isfile(pathname):
r.append(unicodedata.normalize('NFC', pathname))
else:
print 'Skipping %s' % pathname
return r
def main():
display_ignores = False
fh = None
display_matches = False
try:
opts, args = getopt.getopt(sys.argv[1:], "f:ihl", ["file=", "ignores", "help", "list"])
except getopt.GetoptError,exc:
print exc.msg
sys.exit(2)
for opt, arg in opts:
if opt in ("-f", "--file"):
filename = arg
fh = codecs.open(filename, "w", "utf-8")
if opt in ("-i", "--ignores"):
display_ignores = True
if opt in ("-h", "--help"):
usage()
exit()
if opt in ("-l", "--list"):
display_matches = True
dbPath = os.path.expanduser('~/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db')
#Linux DB path - TODO (detect OS or allow specification via command line)
#dbPath = '/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db'
if (not os.path.isfile(dbPath)):
print "Cannot find database at %s. Running PMS locally on this Mac?\nExiting." % (dbPath)
exit()
conn = sqlite3.connect(dbPath)
rows = conn.cursor().execute('select L.id, L.name from library_sections L').fetchall()
for row in rows:
section_id = row[0]
section_name = row[1]
print "\n\nScanning %s[%s]" % (section_name, section_id)
root_paths = conn.cursor().execute('select root_path from section_locations where library_section_id=?', [section_id,]).fetchall()
basePaths = map(lambda p: p[0], root_paths)
print "Composed of %s" % (", ".join(basePaths))
paths = list()
rows = conn.cursor().execute('SELECT mp.file, md.title from media_parts mp JOIN media_items mi ON mp.media_item_id=mi.id JOIN metadata_items md ON mi.metadata_item_id=md.id WHERE mi.library_section_id=?', [section_id,]).fetchall()
paths = map(lambda p: unicodedata.normalize('NFC', p[0]), rows)
if (display_matches):
match_fn = "plex_matches_%s.txt" % (section_id)
print "\tWriting matches to %s..." % (match_fn)
match_fh = codecs.open(match_fn, "w", "utf-8")
rows.sort(key=lambda x: x[1])
for row in rows:
match_fh.write("%s -- %s\n" % (row[1], row[0]))
match_fh.close()
missing = list()
for basePath in basePaths:
print "\tHandling path %s ..." % basePath
filePaths = listTree(basePath)
for filePath in filePaths:
if filePath not in paths:
cext = os.path.splitext(filePath)[1].lower()
fname = os.path.split(filePath)[1]
if (fname in OK_FILES):
#Don't do anything for acceptable files
pass
elif (cext in OTHER_EXTENSIONS):
#ignore images and subtitles
pass
elif (cext not in VIDEO_EXTENSIONS):
#these shouldn't be here
if (display_ignores):
print 'Ignoring %s' % filePath
pass
else:
missing.append(filePath)
print '\tfinished %s' % basePath
print '\nMissing %i items:' % len(missing)
if (fh):
fh.write("\nSection %s:\n" % (section_name))
fh.write("Missing %i items\n" % len(missing))
for item in missing:
print item
if (fh):
fh.write(item+"\n")
if (fh):
fh.close()
conn.close()
exit()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment