Last active
August 29, 2015 14:13
-
-
Save beloso/823a2c6ef0e15432adc2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
#################################################################################### | |
## INFORMATION | |
#################################################################################### | |
## Developed by: Steven Johnson (minor modifications by egretsareherons) | |
## (a minor modifications by aross01) | |
## (a minor modification by STEEVo) | |
## | |
## Last Updated: 02/26/2014 5:05PM MST | |
## | |
## Description: Auto-Delete Watched Items In Plex | |
## | |
## Required Configurations: | |
## - PC (Blank = AutoDetect | W = Windows | L = Linux/Unix/MAC) | |
## - Host (Hostname or IP | Blank = 127.0.0.1) | |
## - Port (Port | Blank = 32400) | |
## - SectionList - A list of sections to operate on. If this is blank, the script | |
## will get a list of all sections from the server & operate on all, except | |
## for the sections listed in IgnoreSections | |
## - IgnoreSections - Only used if SectionList is blank. When SectionList is blank, | |
## the script will operate on all available sections of your install | |
## except for the ones listed here. | |
## - Delete (1 = Delete | Blank = 0 (For Testing or Review)) | |
## - Move (1 = move files to DestinationDir, 0 = don't move) | |
## - Copy (1 = copy files to DestinationDir, 0 = don't copy) | |
## - Shows ( ["Show1","Show2"]; = Kept Shows OR [""]; = DEL ALL SHOWS ) | |
## - OnDeck ( 1 = Keep If On Deck | Blank = Delete Regardless if onDeck ) | |
## - DestinationDir - the place to move or copy your watched files for later manual deletion | |
## - DestinationSection - if your destination dir is also a configured section, set the | |
## number here so TriggerRescan knows to rescan that section when changes are made. | |
## only used if TriggerRescan is True; | |
## - TriggerRescan - automatically trigger a deep rescan of changed sections. True or False. | |
## | |
## - KeepMode (Blank = Disabled | W = "Watched" | A = "Added") | |
## Method used to keep items for X number of days (specified by DaysToKeep below.) | |
## You may choose to base the keep on the last "watched" or "added" date. | |
## - DaysToKeep ("30" = Keep items for 30 days following the KeepMode selected) | |
## | |
#################################################################################### | |
#################################################################################### | |
PC = "" | |
Host = "" | |
Port = "" | |
SectionList = [] | |
IgnoreSections = [] | |
Delete = "" | |
Move = "0" | |
Copy = "0" | |
Shows = [] | |
OnDeck = "" | |
DestinationDir = "" | |
DestinationSection = "" | |
TriggerRescan = True | |
KeepMode = "" | |
DaysToKeep = 30 | |
#################################################################################### | |
## NO NEED TO EDIT BELOW THIS LINE | |
#################################################################################### | |
import os | |
import xml.dom.minidom | |
import platform | |
import re | |
import shutil | |
import datetime | |
import glob | |
try: | |
import urllib.request as urllib2 | |
except: | |
import urllib2 | |
#################################################################################### | |
## Check On Deck | |
#################################################################################### | |
def CheckOnDeck( CheckDeckFile ): | |
InTheDeck = 0 | |
for DeckVideoNode in deck.getElementsByTagName("Video"): | |
DeckMediaNode = DeckVideoNode.getElementsByTagName("Media") | |
for DeckMedia in DeckMediaNode: | |
DeckPartNode = DeckMedia.getElementsByTagName("Part") | |
for DeckPart in DeckPartNode: | |
Deckfile = DeckPart.getAttribute("file") | |
if CheckDeckFile==Deckfile: | |
InTheDeck += 1 | |
else: | |
InTheDeck += 0 | |
return InTheDeck | |
#################################################################################### | |
## Check Shows And Delete If Configured | |
#################################################################################### | |
def CheckShows( CheckFile ): | |
global FileCount | |
global DeleteCount | |
global MoveCount | |
global CopyCount | |
global FlaggedCount | |
global OnDeckCount | |
global ShowsCount | |
global DestinationDir | |
FileCount += 1 | |
CantDelete = 0 | |
ShowFound = "" | |
changed = 0 | |
## -- CHECK SHOWS -- | |
for Show in Shows: | |
Show = re.sub('[^A-Za-z0-9 ]+', '', Show).strip() | |
if Show=="": | |
CantDelete = 0 | |
else: | |
if (' ' in Show) == True: | |
if all(str(Word) in CheckFile for Word in Show.split()): | |
CantDelete += 1 | |
ShowFound = "[" + Show + "]" | |
ShowsCount += 1 | |
else: | |
CantDelete += 0 | |
else: | |
if Show in CheckFile: | |
CantDelete += 1 | |
ShowFound = "[" + Show + "]" | |
ShowsCount += 1 | |
else: | |
CantDelete += 0 | |
## -- Check OnDeck -- | |
if OnDeck=="1": | |
IsOnDeck = CheckOnDeck(CheckFile); | |
if IsOnDeck==0: | |
CantDelete += 0 | |
else: | |
CantDelete += 1 | |
ShowFound = "[OnDeck]" + ShowFound | |
OnDeckCount += 1 | |
## -- Check if video older than days to keep [Viewed method] -- | |
if KeepMode=="W": | |
if DaysToKeep > DaysSinceVideoLastViewed: | |
CantDelete += 1 | |
ShowsCount += 1 | |
# ShowFound = " | Days Since Viewed:" + str(DaysSinceVideoLastViewed) +" |" + ShowFound | |
## -- Check if video older than days to keep [Added method] -- | |
if KeepMode=="A": | |
if DaysToKeep > DaysSinceVideoAdded: | |
CantDelete += 1 | |
ShowsCount += 1 | |
# ShowFound = " | Days Since Added:" + str(DaysSinceVideoAdded) +" |" + ShowFound | |
## -- DELETE SHOWS -- | |
if CantDelete == 0: | |
if Delete=="1": | |
#print("**[DELETED] " + CheckFile) | |
similairFiles = re.sub('\[', '[[]', os.path.splitext(file)[0]) + "*" | |
print("Looking for " + similairFiles) | |
for deleteFile in glob.glob(similairFiles): | |
try: | |
os.remove(deleteFile) | |
print("**[DELETED] " + deleteFile) | |
changed = 1 | |
DeleteCount += 1 | |
except Exception as e: | |
print ("error deleting file: %s" % e) | |
elif Move == "1": | |
try: | |
os.utime(os.path.realpath(CheckFile), None) | |
shutil.move(os.path.realpath(CheckFile), DestinationDir) | |
print("**[MOVED] " + CheckFile) | |
changed = 1 | |
except Exception as e: | |
print ("error moving file: %s" % e) | |
if os.path.islink(CheckFile): | |
os.unlink(CheckFile) | |
MoveCount += 1 | |
elif Copy == "1": | |
try: | |
shutil.copy(os.path.realpath(CheckFile), DestinationDir) | |
changed = 1 | |
except Exception as e: | |
print ("error copying file: %s" % e) | |
print("**[COPIED] " + CheckFile) | |
CopyCount += 1 | |
else: | |
print("**[FLAGGED] " + CheckFile) | |
FlaggedCount += 1 | |
else: | |
print("[KEEPING]") | |
return changed | |
#################################################################################### | |
## Checking URL | |
#################################################################################### | |
if Host=="": | |
Host="127.0.0.1" | |
if Port=="": | |
Port="32400" | |
print("----------------------------------------------------------------------------") | |
print(" Detected Settings") | |
print("----------------------------------------------------------------------------") | |
print("Host: " + Host) | |
print("Port: " + Port) | |
#print("Section: " + Section) | |
#print("URL: " + URL) | |
#print("OnDeck URL: " + OnDeckURL) | |
if KeepMode=="W": | |
print("Keep Mode: Keeping for " + str(DaysToKeep) + " days following the date last viewed") | |
elif KeepMode=="A": | |
print("Keep Mode: Keeping viewed items that were added to the library less than " + str(DaysToKeep) + " days ago") | |
else: | |
print("Keep Mode: DISABLED") | |
#################################################################################### | |
## Checking Shows | |
#################################################################################### | |
NoDelete = " | " | |
ShowCount = len(Shows) | |
print("Show Count: " + str(ShowCount)) | |
for Show in Shows: | |
Show = re.sub('[^A-Za-z0-9 ]+', '', Show).strip() | |
if Show=="": | |
NoDelete += "(None Listed) | " | |
ShowCount -= 1 | |
else: | |
NoDelete += Show + " | " | |
print("Number of Shows Detected For Keeping: " + str(ShowCount)) | |
print ("Shows to Keep:" + NoDelete) | |
################################################################################### | |
## Checking Delete | |
#################################################################################### | |
if Delete=="1": | |
print("Delete: ***Enabled***") | |
else: | |
print("Delete: Disabled - Flagging Only") | |
if OnDeck=="1": | |
print("Delete OnDeck: No") | |
else: | |
print("Delete OnDeck: Yes") | |
#################################################################################### | |
## Checking OS | |
#################################################################################### | |
AD = "" | |
if PC=="": | |
AD = "(Auto Detected)" | |
if platform.system()=="Windows": | |
PC = "W" | |
elif platform.system()=="Linux": | |
PC = "L" | |
elif platform.system()=="Darwin": | |
PC = "L" | |
FileCount = 0 | |
DeleteCount = 0 | |
MoveCount = 0 | |
CopyCount = 0 | |
FlaggedCount = 0 | |
OnDeckCount = 0 | |
ShowsCount = 0 | |
RescannedSections = [] | |
#################################################################################### | |
## Get list of sections | |
#################################################################################### | |
if PC=="L": | |
print("Operating System: Linux " + AD) | |
import urllib2 | |
elif PC=="W": | |
print("Operating System: Windows " + AD) | |
import urllib.request | |
else: | |
print("Operating System: ** Not Configured ** (" + platform.system() + ") is not recognized.") | |
exit() | |
if not SectionList: | |
URL = ("http://" + Host + ":" + Port + "/library/sections/") | |
doc = xml.dom.minidom.parse(urllib2.urlopen(URL)) | |
for Section in doc.getElementsByTagName("Directory"): | |
if Section.getAttribute("key") not in IgnoreSections: | |
SectionList.append(Section.getAttribute("key")) | |
SectionList.sort(key=int) | |
print ("Section List Mode: Auto") | |
print ("Operating on sections: " + ','.join(str(x) for x in SectionList)) | |
print ("Skipping Sections: " + ','.join(str(x) for x in IgnoreSections)) | |
else: | |
print ("Section List Mode: User-defined") | |
print ("Operating on user-defined sections: " + ','.join(str(x) for x in SectionList)) | |
#################################################################################### | |
## Loop on sections | |
#################################################################################### | |
rescan_destination = False | |
for Section in SectionList: | |
Section = str(Section) | |
URL = ("http://" + Host + ":" + Port + "/library/sections/" + Section + "/recentlyViewed") | |
OnDeckURL = ("http://" + Host + ":" + Port + "/library/sections/" + Section + "/onDeck") | |
#################################################################################### | |
## Setting OS Based Variables | |
#################################################################################### | |
if PC=="L": | |
doc = xml.dom.minidom.parse(urllib2.urlopen(URL)) | |
deck = xml.dom.minidom.parse(urllib2.urlopen(OnDeckURL)) | |
elif PC=="W": | |
doc = xml.dom.minidom.parse(urllib.request.urlopen(URL)) | |
deck = xml.dom.minidom.parse(urllib.request.urlopen(OnDeckURL)) | |
SectionName = doc.getElementsByTagName("MediaContainer")[0].getAttribute("title1") | |
print("") | |
print("--------- Section "+ Section +": " + SectionName + " -----------------------------------") | |
#################################################################################### | |
## Get Files for Watched Shows | |
#################################################################################### | |
changed = 0 | |
for VideoNode in doc.getElementsByTagName("Video"): | |
view = VideoNode.getAttribute("viewCount") | |
if view == '': | |
view = 0 | |
view = int(view) | |
################################################################ | |
###Find number of days between date video was viewed and today | |
lastViewedAt = VideoNode.getAttribute("lastViewedAt") | |
d1 = datetime.datetime.today() | |
d2 = datetime.datetime.fromtimestamp(float(lastViewedAt)) | |
DaysSinceVideoLastViewed = (d1 - d2).days | |
################################################################ | |
################################################################ | |
###Find number of days between date video was added and today | |
addedAt = VideoNode.getAttribute("addedAt") | |
d1 = datetime.datetime.today() | |
da2 = datetime.datetime.fromtimestamp(float(addedAt)) | |
DaysSinceVideoAdded = (d1 - da2).days | |
################################################################ | |
MediaNode = VideoNode.getElementsByTagName("Media") | |
for Media in MediaNode: | |
PartNode = Media.getElementsByTagName("Part") | |
for Part in PartNode: | |
file = Part.getAttribute("file") | |
#NEW (Removed by Mustang): | |
#if PC == 'L': | |
#file = urllib2.unquote(file.encode('utf-8')).decode('utf-8') | |
#else: | |
#import urllib | |
#file = urllib.parse.unquote(file.encode('utf-8')).decode('utf-8') | |
if str(view)!="0": | |
print(" ") | |
if KeepMode=="W": | |
if str(view)!="0": | |
print ("Viewed:" + str(view) + "x | Days Since Viewed: " + str(DaysSinceVideoLastViewed) + " | " + file) | |
if view > 0: | |
if os.path.isfile(file): | |
changed += CheckShows(file); | |
else: | |
print("##[NOT FOUND] " + file) | |
if KeepMode=="A": | |
if str(view)!="0": | |
print ("Viewed:" + str(view) + "x | Days Since Added: " + str(DaysSinceVideoAdded) + " | " + file) | |
if view > 0: | |
if os.path.isfile(file): | |
changed += CheckShows(file); | |
else: | |
print("##[NOT FOUND] " + file) | |
if KeepMode=="": | |
if str(view)!="0": | |
print ("Viewed:" + str(view) + "x | Days Since Viewed: " + str(DaysSinceVideoLastViewed) + " Added: " + str(DaysSinceVideoAdded) + " | " + file) | |
if view > 0: | |
if os.path.isfile(file): | |
changed += CheckShows(file); | |
else: | |
print("##[NOT FOUND] " + file) | |
# rescan this section if necessary | |
if TriggerRescan and changed: | |
URL = ("http://" + Host + ":" + Port + "/library/sections/" + Section + "/refresh?deep=1") | |
blah = urllib2.urlopen(URL) | |
RescannedSections.append(SectionName + '(' + Section + ')') | |
rescan_destination = True | |
# rescan destination section if necessary | |
if TriggerRescan and DestinationSection and rescan_destination: | |
URL = ("http://" + Host + ":" + Port + "/library/sections/" + str(DestinationSection) + "/refresh?deep=1") | |
blah = urllib2.urlopen(URL) | |
RescannedSections.append('[destination dir]' + '(' + str(DestinationSection) + ')') | |
print("") | |
print("----------------------------------------------------------------------------") | |
print("----------------------------------------------------------------------------") | |
print(" Summary -- Script Completed Successfully") | |
print("----------------------------------------------------------------------------") | |
print("") | |
print(" Total File Count " + str(FileCount)) | |
print(" Kept Show Files " + str(ShowsCount)) | |
print(" On Deck Files " + str(OnDeckCount)) | |
print(" Deleted Files " + str(DeleteCount)) | |
print(" Moved Files " + str(MoveCount)) | |
print(" Copied Files " + str(CopyCount)) | |
print(" Flagged Files " + str(FlaggedCount)) | |
print(" Rescanned Sections " + ', '.join(str(x) for x in RescannedSections) ) | |
print("") | |
print("----------------------------------------------------------------------------") | |
print("----------------------------------------------------------------------------") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment