#!/usr/bin/env python
# Episode rename 0.8 - Copyright 2008 Stavros Korokithakis
# Heavily modified by Rafael Fonseca
# Released under the GNU GPL.
# You can find the latest version at http://www.poromenos.org
import urllib
import urllib2
import optparse
import re
import os
import sys
import subprocess
import random
import shutil
import htmlentitydefs
import hashlib
from datetime import date
class Show:
def __init__(self, title="", rating=0):
self.title = title
self.attributes = {}
self.episodes = {}
def search_show(name):
"""Search Google for the page best matching the given show name."""
google_url = "http://www.google.com/search?q=site%%3Aepguides.com+%s" % urllib.quote(name)
# Bastard Google...
request = urllib2.Request(google_url)
request.add_header("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4")
page = urllib2.urlopen(request).read()
try:
show_id = re.search("epguides.com\/(.*?)\/", page).group(1)
except AttributeError:
try:
show_id = re.search("epguides.com\/(.*?)\/", page).group(0)
except AttributeError:
print "Could not find show title for %s, cannot continue." % name
sys.exit()
return show_id
def parse_epguides(page):
"""Parse an epguides page."""
page = page.replace("\n", "")
page = page.replace("'","'")
show = Show()
d = date.today()
try:
show.title = re.search("""<h1><a href="http://.*?">(.*?)</a></h1>""", page).groups()[0]
except AttributeError:
print "Could not find show title, cannot continue."
sys.exit()
matches = re.search("""<td>aired from: <em>.* (\d+)</em><br />to: <em>.*</em></td>""", page)
try:
year = matches.group(1)
year = 2000 + int(year)
if year > d.year:
year = year - 100
except IndexError:
year = None
show.attributes["year"] = year
episodes = re.findall("\d+. +(?P<season>\d+) *\- *(?P<episode>\d+).*?(?P<day>\d+)(?:\/|\s)+(?P<month>[A-Za-z]+)(?:\/|\s)+(?P<year>\d+) +(?:<a [^>]+><img [^\r\n]+></a>)? ?<a [^>]*>(?P<name>.*?)</a>", page)
month_dict = {"Jan": 1,
"Feb": 2,
"Mar": 3,
"Apr": 4,
"May": 5,
"Jun": 6,
"Jul": 7,
"Aug": 8,
"Sep": 9,
"Oct": 10,
"Nov": 11,
"Dec": 12}
for season, episode, day, month, year, name in episodes:
show.episodes[(int(season), int(episode))] = {"title": name}
#print "DEBUG: s%s e%s" % (season, episode)
year = 2000 + int(year)
if year > d.year:
year = year - 100
try:
release_date = "%s-%02d-%02dT12:00:00Z" % (int(year),month_dict[month],int(day))
except TypeError:
continue
show.episodes[(int(season), int(episode))]["year"] = release_date
return show
def rename_file(filename, show, file_mask, preview=False, use_ap=False, force=False):
series_parser = [
re.compile("^.*?s *(?P<series>\d+) *e *(?P<episode>\d+).*\.(?P<extension>.*?)$", re.IGNORECASE),
re.compile("^.*?(?P<series>\d+)x(?P<episode>\d+).*\.(?P<extension>.*?)$", re.IGNORECASE),
re.compile("^(?:.*?\D|)(?P<series>\d{1,2})(?P<episode>\d{2})(?:\D.*|)\.(?P<extension>.*?)$", re.IGNORECASE),
]
for parser in series_parser:
matches = parser.search(filename)
try:
match_dict = matches.groupdict()
break
except AttributeError:
continue
else:
return
series = int(match_dict["series"])
episode = int(match_dict["episode"])
extension = match_dict["extension"]
info_dictionary = {"show": show.title,
"series_num": series,
"episode_num": episode,
"extension": extension}
try:
info_dictionary.update(show.episodes[(series, episode)])
new_filename = file_mask % info_dictionary
except KeyError:
print 'Episode name for "%s" not found.' % filename
sys.exit()
new_filename = re.sub("[\\\/\:\*\"\?\<\>\|]", "", new_filename)
if new_filename == filename:
if not force:
print 'No changes to "%s".' % filename
sys.exit()
try:
print "Renaming \"%s\" to \"%s\"..." % (filename, new_filename.encode("ascii", "replace"))
except UnicodeDecodeError:
print "Renaming \"%s\" to \"%s\"..." % (filename, new_filename)
if not preview:
if use_ap:
# The temp_filename shenanigans are necessary because AP sometimes
# chokes if it's set to overwrite the file.
temp_filename = filename + str(random.randint(10000, 99999))
arguments = ["AtomicParsley",
filename,
"-o", temp_filename,
"--TVShowName", show.title,
"--stik", "TV Show",
"--TVSeasonNum", str(series),
"--TVEpisodeNum", str(episode),
"--TVEpisode", show.episodes[(series, episode)]["title"],
"--title", show.episodes[(series, episode)]["title"]]
if "year" in show.episodes[(series, episode)]:
arguments.extend(["--year", show.episodes[(series, episode)]["year"]])
elif "year" in show.attributes:
arguments.extend(["--year", show.attributes["year"]])
artwork_file = None
if "artwork" in show.episodes[(series, episode)]:
artwork_filename = hashlib.md5(str(random.randint(10000, 100000))).hexdigest()
artwork_file = open(artwork_filename, "wb")
artwork_file.write(show.episodes[(series, episode)]["artwork"])
artwork_file.close()
arguments.extend(["--artwork", "REMOVE_ALL", "--artwork", artwork_filename])
elif "artwork" in show.attributes:
artwork_filename = hashlib.md5(str(random.randint(10000, 100000))).hexdigest()
artwork_file = open(artwork_filename, "wb")
artwork_file.write(show.attributes["artwork"])
artwork_file.close()
arguments.extend(["--artwork", "REMOVE_ALL", "--artwork", artwork_filename])
proc = subprocess.Popen(tuple(arguments))
proc.wait()
if artwork_file:
os.remove(artwork_filename)
try:
os.rename(temp_filename, new_filename)
except:
print "There was an error while renaming the file."
sys.exit()
if new_filename != filename:
os.remove(filename)
else:
try:
os.rename(filename, new_filename)
except:
print "There was an error while renaming the file."
sys.exit()
def analyze_file(filename,options):
# title_parser = re.compile("(?P<title>^.*)(s|\.|-)(?P<series>\d+)(e|x)(?P<episode>\d+).*\.(?P<extension>.*?)$", re.IGNORECASE)
title_parser = [
re.compile("^(?P<title>.*?)s *(?P<series>\d+) *e *(?P<episode>\d+).*\.(?P<extension>.*?)$", re.IGNORECASE),
re.compile("^(?P<title>.*?)(?P<series>\d+)x(?P<episode>\d+).*\.(?P<extension>.*?)$", re.IGNORECASE),
re.compile("^(?P<title>.*?\D|)(?P<series>\d{1,2})(?P<episode>\d{2})(?:\D.*|)\.(?P<extension>.*?)$", re.IGNORECASE),
]
for parser in title_parser:
matches = parser.search(filename)
try:
title_dict = matches.groupdict()
break
except AttributeError:
continue
else:
return
title = title_dict["title"]
title = title.strip()
title = title.replace('-','.')
title = title.replace(' ','.')
if title.endswith('.'):
title = title.rstrip('.')
title = title.strip()
title = title.split('.')
show_id = search_show("+".join(title))
if not show_id:
return
page_url = "http://epguides.com/%s/" % show_id
request = urllib2.Request(page_url)
if options.use_cookies:
request.add_header("Cookie","ListDisplay=tvrage.com")
try:
page = urllib2.urlopen(request).read()
except urllib2.HTTPError, error:
print "An HTTP error occurred, HTTP code %s." % error.code
sys.exit()
show = parse_epguides(page)
if not show:
sys.exit()
rename_file(filename, show, options.mask, options.preview, options.use_atomic_parsley, options.force)
def main():
parser = optparse.OptionParser(usage="%prog [options] <filename>", version="Episode rename 0.8\nThis program is released under the GNU GPL.")
parser.add_option("-p",
"--preview",
dest="preview",
action="store_true",
help="don't actually rename anything")
parser.add_option("-a",
"--use-atomic-parsley",
dest="use_atomic_parsley",
action="store_true",
help="use AtomicParsley to fill in the files' tags")
parser.add_option("-f",
"--force",
dest="force",
action="store_true",
help="force renaming even if filename doesn't change")
parser.add_option("-c",
"--cookies",
dest="use_cookies",
action="store_true",
help="use site cookies for TVRage")
parser.add_option("-m",
"--mask",
dest="mask",
default="%(show)s - S%(series_num)02dE%(episode_num)02d - %(title)s.%(extension)s",
metavar="MASK",
action="store",
type="string",
help="the filename mask to use when renaming (default: \"%default\")")
parser.set_defaults(preview=False)
parser.set_defaults(force=False)
parser.set_defaults(use_cookies=False)
(options, arguments)=parser.parse_args()
if len(arguments) != 1:
parser.print_help()
sys.exit(1)
filename = arguments[0]
filename = filename.replace('./','')
analyze_file(filename,options)
return sys.stdout
if __name__ == "__main__":
main()