Way to rename based off of episode name?
Opened this issue · 3 comments
Hey folks,
Say I have a series that is always named like
SXEY - <Episode Title>.mp4
so for example:
S1E2 - A Good Night.mp4
Would be season 1, episode 2 titled "A Good Night".
Now the issue i have is that the season and episode are almost certainly wrong, BUT the episode name is correct (or as close as it can be based of of weird characters, etc.).
Is there a way to convince tvnamer to rename to the correct Season/Episode just based off the title (and of course I'd give it the tvdbid or name via command line args to disambiguate what show it is)?
Thanks!
Upon further investigation this is really similar to #193 except in my case i have an incorrect season/episode instead of a correct series name.
My reading of the relevant code suggests that a season/episode combo or date is required. And since your season/episode is unreliable I don't think you could include it in the regex without leading tvnamer astray. Even if you managed to pass that test you need to have a series name in the regex, but that's somewhat easier to fix if you know what series these all came from.
I haven't used this in a long time, but it used to work. I believe the reason I haven't used it recently is because you now need an api key and I was being lazy.
#!/usr/bin/env python3
import os
import sys
import re
import string
from tvdb_api import Tvdb
from titlecase import titlecase
log = open('log.txt', 'w')
global autoRename
autoRename = False # This is used to auto-rename
def get_show_name(path):
#use the known file structure to determine the show name.
show = re.sub('^\/.+\/Series/?','',path)
return re.sub(r'\.',r' ',show)
def process_file(video):
#Use regex to determine season, episode, and name of episode.
season = None
episode = None
name = None
try:
matches = re.search('^S(\d+)E(\d+)\.(.+)\.S(\d+)E(\d+)\.(.+)\.\w{3}$',
video)
season = [int(matches.group(1)), int(matches.group(4))]
episode = [int(matches.group(2)), int(matches.group(5))]
name = [matches.group(3), matches.group(6)]
name = [re.sub(r'\.',r' ', name[0]), re.sub(r'\.',r' ', name[1])]
name = [re.sub(r'([-&])',r' \1 ', name[0]),
re.sub(r'([-&])',r' \1 ', name[1])]
name = [re.sub(r',',r', ', name[0]), re.sub(r',',r', ', name[1])]
except:
matches = re.search('^S(\d+)E(\d+)\.(.+)\.\w{3}$',video)
try:
season = int(matches.group(1))
except:
print("Couldn't determine the season for {}.".format(video))
sys.exit(1)
try:
episode = int(matches.group(2))
except:
print("Couldn't determine the episode for {}.".format(video))
sys.exit(1)
try:
name = matches.group(3)
except:
print("Couldn't determine the episode name for {}.".format(video))
sys.exit(1)
name = re.sub(r'\.',r' ', name)
name = re.sub(r'\s?([-&])\s',r' \1 ', name)
name = re.sub(r',',r', ', name)
name = re.sub(r'([MD]r|Mrs|Ms)\s', r'\1. ', name)
return (season, episode, name)
def is_correct(show, season, episode, name):
#check if the given episode has the correct season and episode numbers
series = show['seriesname']
if isinstance(name, list):
print('Found {} - Season {:02d} Episode {:02d} {}'.format(series,
season[0],
episode[0],
name[0]))
print('\tSeason {:02d} Episode {:02d} {} '.format(series,
season[1],
episode[1],
name[1]))
print('Checking if it has the correct season and episode numbers.')
ep1 = check_episode(series, show, season[0], episode[0], name[0])
ep2 = check_episode(series, show, season[1], episode[1], name[1])
if ep1 is None and ep2 is None:
return None
elif ep1 is None:
ep1 = 'S{:02d}E{:02d}.{}'.format(season[0], episode[0], name[0])
ep1 = re.sub(r', ', r',', ep1)
ep1 = re.sub(r' ([-&]) ', r'\1', ep1)
ep1 = re.sub(r' ', r'.', ep1)
elif ep2 is None:
ep2 = 'S{:02d}E{:02d}.{}'.format(season[1], episode[1], name[1])
ep2 = re.sub(r', ', r',', ep2)
ep2 = re.sub(r' ([-&]) ', r'\1', ep2)
ep2 = re.sub(r' ', r'.', ep2)
return ep1 + '.' + ep2
else:
print('Found {} - Season {:02d} Episode {:02d} {}'.format(series, season, episode, name))
print('Checking if it has the correct season and episode numbers.')
return check_episode(series, show, season, episode, name)
def check_episode(series, show, season, episode, name):
(seasonNumber, episodeNumber, episodeName) = find_episode(show, name)
if seasonNumber and episodeNumber and episodeName:
if not (season == seasonNumber and episode == episodeNumber):
print("Either the season or the episode didn't match.")
print('Found {} - S{:02d}E{:02d} {} instead.'.format(series,
seasonNumber,
episodeNumber,
episodeName))
episodeName = re.sub(r', ', r',', episodeName)
episodeName = re.sub(r' ([-&]) ', r'\1', episodeName)
episodeName = re.sub(r' ', r'.', episodeName)
return ('S{:02d}E{:02d}.{}'.format(seasonNumber, episodeNumber,
episodeName))
elif name != episodeName: #This will force things to titlecase
episodeName = re.sub(r', ', r',', episodeName)
episodeName = re.sub(r' ([-&]) ', r'\1', episodeName)
episodeName = re.sub(r' ', r'.', episodeName)
return ('S{:02d}E{:02d}.{}'.format(seasonNumber, episodeNumber,
episodeName))
else: #season and episode numbers were correct
print('The season and episode are correct. Moving on.')
return None
else: #Couldn't find an episode with this name.
return None
def find_episode(show, name):
#Find the season and episode numbers from the given episode name.
result = show.search(name, key='episodeName')
if result:
return process_result(result, name)
else: #try searching for name without space around -
tmp = re.sub(r' (-) ',r'\1',name)
tmp = re.sub(r' (:) ',r'\1 ',tmp)
result = show.search(tmp, key='episodeName')
if result:
return process_result(result, tmp)
else:
tmp = re.sub(r'([MD]r|Mrs|Ms)\. ',r'\1 ', name)
result = show.search(tmp, key='episodeName')
if result:
name = tmp
return process_result(result, name)
if not result:
print('Couldn\'t find an episode with this name.')
log.write('{} - {}\n'.format(show['seriesname'], name))
return (None,None,None)
def process_result(result, name):
for episode in result:
if name.lower() == episode['episodename'].lower():
seasonNumber = int(episode['seasonnumber'])
episodeNumber = int(episode['episodenumber'])
episodeName = titlecase(episode['episodename'])
return (seasonNumber, episodeNumber, episodeName)
print('No episode with this name appears to exist for this series.')
alphabet = string.ascii_lowercase
i = 0
s = ''
for episode in result:
i += 1
epStr = ('S{:02d}E{:02d}'.format(int(episode['seasonnumber']),
int(episode['episodenumber'])))
s += ('({}) {} - {}\n'.format(str(i),epStr,episode['episodename']))
s += ('({}) None of the above.'.format(str(i+1)))
lst = [str(j) for j in range(1,i+2)]
ans = ask('Which episode would you like?\n' + s, lst, str(i+1))
if ans == str(i+1):
print('No episode selected. Skipping this episode.')
return (None,None,None)
else:
ans = int(ans) - 1 #convert back to list index
seasonNumber = int(result[ans]['seasonnumber'])
episodeNumber = int(result[ans]['episodenumber'])
episodeName = titlecase(result[ans]['episodename'])
response = ask('Use the new episode name?', ['y', 'n'], 'y')
if response == 'y':
print('Using new episode name.')
return (seasonNumber, episodeNumber, episodeName)
else:
print('Using old episode name.')
return (seasonNumber, episodeNumber, name)
return (None,None,None)
def move_file(f1, f2, path):
global autoRename
if f1 != f2:
response = ask('Move {} to {}?\nPath is {}'.format(f1,f2,path), ['y','n','a','q'],
'y')
if response == 'a':
autoRename = True
response = 'y'
if response == 'y' or autoRename:
print('Renaming file.')
source = ('{}/{}'.format(path,f1))
destination = ('{}/{}'.format(path,f2))
os.rename(source,destination)
elif response == 'q':
print('Quitting')
raise SystemExit
def ask(question, options, default = "y"):
"""
Takes a question (string), list of options and a default value (used
when user simply hits enter).
Asks until valid option is entered.
"""
# Highlight default option with [ ]
options_str = []
for x in options:
if x == default:
x = "[{}]".format(x)
if x != '':
options_str.append(x)
options_str = "/".join(options_str)
while True:
print(question)
print("({})".format(options_str))
try:
ans = input().strip()
except KeyboardInterrupt:
print("\n", sys.exc_info())
raise UserAbort(sys.exc_info())
if ans in options:
return ans
elif ans == '':
return default
def usage():
# print usage
print('Usage: ./SeriesRenamer.py <optional directory>')
def main(args):
if len(args) == 1:
top_directory = args[0]
else:
usage()
return 1
tvdb = Tvdb()
for path, dirnames, filenames in os.walk(top_directory):
show = get_show_name(path)
if not show: #skip if can't find show
continue
series = show
show = tvdb[show]
for video in filenames:
#get the original extension
ext = os.path.splitext(video)[-1]
print('\nProcessing file: {}'.format(video))
#only process video files
if not (ext.lower() in ('.vob','.avi', '.mp4', '.mpg', '.mkv', '.flv')):
continue
(season, episode, name) = process_file(video)
if name:
newFile = is_correct(show, season, episode, name)
if newFile:
newFile += ext #tack our extension back on
print(newFile)
move_file(video, newFile, path)
else:
print('Couldn\'t determine the episode name.')
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))