]> gitweb.pimeys.fr Git - scripts-20-100.git/blob - rename_series.py
typo
[scripts-20-100.git] / rename_series.py
1 #!/usr/bin/env python
2 # -*- encoding: utf-8 -*-
3
4 """ Pour renommer en masse les épisodes d'une série à partir d'un fichier
5 de titres. """
6
7 import re
8 import os
9 import os.path
10 import argparse
11 import pprint
12
13 FORMAT = "%%(series_name)s - [%%(season)0%(logmaxseason)sdx%%(episode)0%(logmaxepisode)sd] - %%(title)s.%%(extension)s"
14
15 SEASONEPISODE_REGEXP = ur"^(?P<series_name>([A-Za-z0-9_()-]+(\.| ))+)(S|- \[?)(?P<season>\d+)(E|x)(?P<episode>\d+).*\.(?P<extension>[a-z0-9]*)$"
16 sep_re = re.compile(SEASONEPISODE_REGEXP)
17
18 def _tryint(s):
19 """Renvoie int(s) ou s si échec."""
20 try:
21 return int(s)
22 except:
23 return s
24
25 def get_titles(filename, debug=False, oneintwo=False):
26 """Récupère les titres des épisodes dans le fichier"""
27 text = open(filename).read()
28 if oneintwo:
29 # On enlève une ligne sur deux
30 text = re.sub("(.*\n).*\n", r'\1', text)
31 titles = re.findall('^([0-9\s]+)"(.*?)"', text, flags=re.MULTILINE)
32 titles = {_tryint(n) : title for (n, title) in titles}
33 if debug:
34 print "Titres :"
35 pprint.pprint(titles)
36 return titles
37
38 def proper_name(name):
39 """Proprifie le nom de la série."""
40 return name.replace(".", " ").strip()
41
42 def get_files(titlesfilename, debug=False):
43 """Récupère les fichiers locaux, en enlevant le fichier de titres.
44 Les analyse à coup de regexp et les renvoie dans un dico. Clé = n° de l'épisode."""
45 files = os.listdir(".")
46 if titlesfilename in files:
47 files.remove(titlesfilename)
48 dir = {}
49 for filename in files:
50 o = sep_re.match(filename)
51 if o:
52 d = o.groupdict()
53 d["episode"] = int(d["episode"])
54 d["season"] = int(d["season"])
55 if dir.has_key(d["episode"]):
56 raise ValueError("Épisode %s trouvé deux fois." % d["episode"])
57
58 dir[d["episode"]] = (filename, d)
59 else:
60 raise ValueError("Unparsable file : %s" % filename)
61 if debug:
62 print "Fichiers :"
63 pprint.pprint(dir)
64 return dir
65
66 def process(dir, titles, maxseason, maxepisode, seriesname, args):
67 """Prépare tous les renommages.
68 Renvoie une map before -> after."""
69 series_names = set([proper_name(d["series_name"]) for (_, d) in dir.values()])
70 if seriesname:
71 series_name = seriesname
72 elif len(series_names) > 1:
73 print "Plusieurs titres trouvés :"
74 print list(series_names)
75 series_name = raw_input("Spécifier titre : ")
76 else:
77 series_name = list(series_names)[0]
78 l = range(args.starting, args.starting + len(dir))
79 eps = dir.keys()
80 eps.sort()
81 if eps != l:
82 raise ValueError("N° titres ≠ N° épisodes : %s %s" % (l, eps))
83 if len(titles) != len(eps):
84 print "Pas autant de titres que d'épisodes : on retente en lisant une ligne sur deux."
85 titles = get_titles(args.titles, oneintwo=True)
86 if len(titles) != len(eps):
87 raise ValueError("Pas autant de titres que d'épisodes !")
88 formatdir = {
89 "logmaxseason" : len(str(maxseason)),
90 "logmaxepisode" : max(len(str(max(eps))), len(str(maxepisode))),
91 "series_name" : series_name
92 }
93 map = []
94 for k in eps:
95 (before, d) = dir[k]
96 d.update(formatdir)
97 d["title"] = titles[k]
98 newname = FORMAT % d
99 newname = newname % d
100 map.append((before, newname))
101 return map
102
103 def confirm(map):
104 """Affiche ce qui va être fait et demande confirmation."""
105 size = max([len(b) for (b, a) in map])
106 for (b, a) in map:
107 print "%s%s %s" % (b, " " * (size - len(b)), a)
108 ans = raw_input("Renommer ?")
109 return ans.lower() in ["y", "o", "yes", "oui"]
110
111
112 def do(map):
113 """Fait le renommage."""
114 for (before, after) in map:
115 os.rename(before, after)
116
117 if __name__ == "__main__":
118 parser = argparse.ArgumentParser(description="Renommage en batch d'épisodes de série.")
119 parser.add_argument('-v', '--verbose', help="Afficher du debug", action="store_true")
120 parser.add_argument('-q', '--quiet', help="Ne pas afficher les warnings", action="store_true")
121 parser.add_argument('-t', '--titles', help="Fichier contenant les titres des épisodes", action="store", type=str, default="titles")
122 parser.add_argument('-m', '--maxseason', help="Nombre total de saisons (pour le nombre de 0)", action="store", type=str, default=1)
123 parser.add_argument('-M', '--maxepisode', help="Nombre total d'épisodes (pour le nombre de 0)", action="store", type=str, default=10)
124 parser.add_argument('-s', '--seriesname', help="Override le nom de la série", action="store", type=str)
125 parser.add_argument('-n', '--starting', help="Numéro du 1er épisode", action="store", type=int, default=1)
126
127 args = parser.parse_args()
128
129 titles = get_titles(args.titles, args.verbose)
130 files = get_files(args.titles, args.verbose)
131 map = process(files, titles, args.maxseason, args.maxepisode, args.seriesname, args)
132 if confirm(map):
133 do(map)