X-Git-Url: https://git.distorted.org.uk/~mdw/epls/blobdiff_plain/0c4ca4f3e3b627849cda630e7e6bff70a133e11e..dcb1cc6cc8b41a7899e61c81f769b08083505678:/mkm3u diff --git a/mkm3u b/mkm3u index 7cf240c..08ef4ff 100755 --- a/mkm3u +++ b/mkm3u @@ -149,27 +149,26 @@ class VideoSeason (object): raise ExpectedError("season %d episode %d already taken" % (me.i, i)) me.episodes[i] = disc; disc.neps += 1 -def some_group(m, *gg): - for g in gg: - s = m.group(g) +def match_group(m, *groups, dflt = None, mustp = False): + for g in groups: + try: s = m.group(g) + except IndexError: continue if s is not None: return s - return None + if mustp: raise ValueError("no match found") + else: return dflt class VideoDir (object): - _R_ISO_PRE = RX.compile(r""" ^ - (?: S (?P \d+) - (?: \. \ (?P .*) — (?: D \d+ \. \ )? | - D \d+ \. \ | - (?= E \d+ \. \ ) | - \. \ ) | - \d+ \. \ ) - (?: (?P - (?: S \d+ \ )? E \d+ (?: – \d+)? - (?: , \ (?: S \d+ \ )? E \d+ (?: – \d+)?)*) | - (?P E \d+) \. \ .*) - \. iso $ - """, RX.X) + _R_ISO_PRE = list(map(lambda pat: RX.compile("^" + pat + r"\.iso$", RX.X), + [r""" S? (?P \d+) [A-Z]? \. \ (?P .*) — + (?: D \d+ \. \ )? + (?P .*) """, + r""" S (?P \d+) (?: D \d+)? \. \ (?P .*) """, + r""" S (?P \d+) \. \ (?P E \d+ .*) """, + r""" S (?P \d+) \. \ (?P E \d+ .*) """, + r""" S (?P \d+) (?P E \d+) \. \ .* """, + r""" \d+ \. \ (?P [ES] \d+ .*) """, + r""" (?P \d+ ) \. \ .* """])) _R_ISO_EP = RX.compile(r""" ^ (?: S (?P \d+) \ )? @@ -185,22 +184,26 @@ class VideoDir (object): for fn in fns: path = OS.path.join(dir, fn) if not fn.endswith(".iso"): continue - m = me._R_ISO_PRE.match(fn) - if not m: - #print(";; `%s' ignored" % path, file = SYS.stderr) + #print(";; `%s'" % path, file = SYS.stderr) + for r in me._R_ISO_PRE: + m = r.match(fn) + if m: break + else: + #print(";;\tignored (regex mismatch)", file = SYS.stderr) continue - i = filter(m.group("si"), int) - stitle = m.group("st") - check(i is not None or stitle is None, + si = filter(match_group(m, "si"), int) + stitle = match_group(m, "stitle") + + check(si is not None or stitle is None, "explicit season title without number in `%s'" % fn) - if i is not None: - if season is None or i != season.i: - check(season is None or i == season.i + 1, + if si is not None: + if season is None or si != season.i: + check(season is None or si == season.i + 1, "season %d /= %d" % - (i, season is None and -1 or season.i + 1)) - check(i not in seasons, "season %d already seen" % i) - seasons[i] = season = VideoSeason(i, stitle) + (si, season is None and -1 or season.i + 1)) + check(si not in seasons, "season %d already seen" % si) + seasons[si] = season = VideoSeason(si, stitle) else: check(stitle == season.title, "season title `%s' /= `%s'" % (stitle, season.title)) @@ -208,15 +211,15 @@ class VideoDir (object): disc = VideoDisc(path) ts = season any, bad = False, False - epname = m.group("epname") - if epname is not None: eplist = [epname] - else: eplist = m.group("eplist").split(", ") + epnum = match_group(m, "epnum") + if epnum is not None: eplist = ["E" + epnum] + else: eplist = match_group(m, "epex", mustp = True).split(", ") for eprange in eplist: mm = me._R_ISO_EP.match(eprange) - if mm is None: bad = True; continue - if not any: - #print(";; `%s'" % path, file = SYS.stderr) - any = True + if mm is None: + #print(";;\t`%s'?" % eprange, file = SYS.stderr) + bad = True; continue + if not any: any = True i = filter(mm.group("si"), int) if i is not None: try: ts = seasons[i] @@ -228,8 +231,11 @@ class VideoDir (object): for k in range(start, end + 1): ts.set_episode_disc(k, disc) #print(";;\tepisode %d.%d" % (ts.i, k), file = SYS.stderr) - if not any: pass #print(";; `%s' ignored" % path, file = SYS.stderr) - elif bad: raise ExpectedError("bad ep list in `%s'", fn) + if not any: + #print(";;\tignored", file = SYS.stderr) + pass + elif bad: + raise ExpectedError("bad ep list in `%s'", fn) me.seasons = seasons class AudioDisc (Source): @@ -275,12 +281,13 @@ class Chapter (object): me.url = episode.source.url(episode.tno, i, i + 1) class Episode (object): - def __init__(me, season, i, neps, title, src, tno = None, startch = None): + def __init__(me, season, i, neps, title, src, tno = None, + startch = None, endch = None): me.season = season me.i, me.neps, me.title = i, neps, title me.chapters = [] me.source, me.tno = src, tno - me.url = src.url(tno, startch, None) + me.url = src.url(tno, startch, endch) def add_chapter(me, title, j): ch = Chapter(me, title, j) me.chapters.append(ch) @@ -294,8 +301,8 @@ class BaseSeason (object): me.episodes = [] me.implicitp = implicitp me.ep_i, episodes = 1, [] - def add_episode(me, j, neps, title, src, tno, startch): - ep = Episode(me, j, neps, title, src, tno, startch) + def add_episode(me, j, neps, title, src, tno, startch, endch): + ep = Episode(me, j, neps, title, src, tno, startch, endch) me.episodes.append(ep) src.nuses += neps; me.ep_i += neps return ep @@ -326,10 +333,10 @@ class MovieSeason (BaseSeason): super().__init__(series, *args, **kw) me.title = title me.i = None - def add_episode(me, j, neps, title, src, tno, startch): + def add_episode(me, j, neps, title, src, tno, startch, endch): if me.title is None and title is None: raise ExpectedError("movie or movie season must have a title") - return super().add_episode(j, neps, title, src, tno, startch) + return super().add_episode(j, neps, title, src, tno, startch, endch) def _eplabel(me, i, neps, title): if me.title is None: label = title @@ -341,10 +348,11 @@ class MovieSeason (BaseSeason): return label class Series (object): - def __init__(me, playlist, title = None): + def __init__(me, playlist, name, title = None, wantedp = True): me.playlist = playlist - me.title = title + me.name, me.title = name, title me.cur_season = None + me.wantedp = wantedp def _add_season(me, season): me.cur_season = season def add_season(me, title, i, implicitp = False): @@ -420,7 +428,7 @@ class EpisodeListParser (object): me._set_mode(MODE_SINGLE) try: series = me._series[None] except KeyError: - series = me._series[None] = Series(me._pl) + series = me._series[None] = Series(me._pl, None) me._pl.nseries += 1 else: me._set_mode(MODE_MULTI) @@ -432,10 +440,19 @@ class EpisodeListParser (object): for k, v in me._keyvals(opts): if k is None: name = v else: me._bad_keyval(cmd, k, v) - return me._get_series(name), name + return me._get_series(name) - def _wantedp(me, name): - return me._series_wanted is None or name in me._series_wanted + def _auto_epsrc(me, series): + dir = lookup(me._vdirs, series.name, "no active video directory") + season = series.ensure_season() + check(season.i is not None, "must use explicit iso for movie seasons") + vseason = lookup(dir.seasons, season.i, + "season %d not found in video dir `%s'" % + (season.i, dir.dir)) + src = lookup(vseason.episodes, season.ep_i, + "episode %d.%d not found in video dir `%s'" % + (season.i, season.ep_i, dir.dir)) + return src def _process_cmd(me, ww): @@ -453,19 +470,21 @@ class EpisodeListParser (object): check(name not in me._series, "series `%s' already defined" % name) title = ww.rest(); check(title is not None, "missing title") me._set_mode(MODE_MULTI) - me._series[name] = Series(me._pl, title) - if me._wantedp(name): me._pl.nseries += 1 + me._series[name] = series = Series(me._pl, name, title, + me._series_wanted is None or + name in me._series_wanted) + if series.wantedp: me._pl.nseries += 1 elif cmd == "season": - series, sname = me._opts_series(cmd, opts) + series = me._opts_series(cmd, opts) w = ww.nextword(); check(w is not None, "missing season number") if w == "-": - if not me._wantedp(sname): return + if not series.wantedp: return series.add_movies(ww.rest()) else: title = ww.rest(); i = getint(w) - if not me._wantedp(sname): return + if not series.wantedp: return series.add_season(ww.rest(), getint(w), implicitp = False) me._cur_episode = me._cur_chapter = None me._pl.done_season() @@ -479,49 +498,40 @@ class EpisodeListParser (object): me._pl.epname, me._pl.epnames = name, names elif cmd == "epno": - series, sname = me._opts_series(cmd, opts) + series = me._opts_series(cmd, opts) w = ww.rest(); check(w is not None, "missing episode number") epi = getint(w) - if not me._wantedp(sname): return + if not series.wantedp: return series.ensure_season().ep_i = epi elif cmd == "iso": - _, name = me._opts_series(cmd, opts) + series = me._opts_series(cmd, opts) fn = ww.rest(); check(fn is not None, "missing filename") - if not me._wantedp(name): return - if fn == "-": forget(me._isos, name) + if not series.wantedp: return + if fn == "-": forget(me._isos, series.name) else: check(OS.path.exists(OS.path.join(ROOT, fn)), "iso file `%s' not found" % fn) - me._isos[name] = VideoDisc(fn) + me._isos[series.name] = VideoDisc(fn) elif cmd == "vdir": - _, name = me._opts_series(cmd, opts) + series = me._opts_series(cmd, opts) dir = ww.rest(); check(dir is not None, "missing directory") - if not me._wantedp(name): return - if dir == "-": forget(me._vdirs, name) - else: me._vdirs[name] = VideoDir(dir) + if not series.wantedp: return + if dir == "-": forget(me._vdirs, series.name) + else: me._vdirs[series.name] = VideoDir(dir) elif cmd == "adir": - _, name = me._opts_series(cmd, opts) + series = me._opts_series(cmd, opts) dir = ww.rest(); check(dir is not None, "missing directory") - if not me._wantedp(name): return - if dir == "-": forget(me._audirs, name) - else: me._audirs[name] = AudioDir(dir) + if not series.wantedp: return + if dir == "-": forget(me._audirs, series.name) + else: me._audirs[series.name] = AudioDir(dir) elif cmd == "displaced": - series, name = me._opts_series(cmd, opts) + series = me._opts_series(cmd, opts) w = ww.rest(); check(w is not None, "missing count"); n = getint(w) - check(name not in me._isos, "iso file active") - season = series.ensure_season() - check(season.i is not None, "movie season active") - dir = lookup(me._vdirs, name, "no active video directory") - vseason = lookup(dir.seasons, season.i, - "season %d not found in video dir `%s'" % - (season.i, dir.dir)) - src = lookup(vseason.episodes, season.ep_i, - "episode %d.%d not found in video dir `%s'" % - (season.i, season.ep_i, dir.dir)) + src = me._auto_epsrc(series) src.nuses += n else: raise ExpectedError("unknown command `%s'" % cmd) @@ -529,7 +539,7 @@ class EpisodeListParser (object): def _process_episode(me, ww): opts = ww.nextword(); check(opts is not None, "missing title/options") - ti = None; sname = None; neps = 1; epi = None; ch = None + ti = None; sname = None; neps = 1; epi = None; loch = hich = None for k, v in me._keyvals(opts): if k is None: if v.isdigit(): ti = int(v) @@ -538,38 +548,32 @@ class EpisodeListParser (object): elif k == "s": sname = v elif k == "n": neps = getint(v) elif k == "ep": epi = getint(v) - elif k == "ch": ch = getint(v) + elif k == "ch": + try: sep = v.index("-") + except ValueError: loch, hich = getint(v), None + else: loch, hich = getint(v[:sep]), getint(v[sep + 1:]) + 1 else: raise ExpectedError("unknown episode option `%s'" % k) check(ti is not None, "missing title number") series = me._get_series(sname) me._cur_chapter = None title = ww.rest() - if not me._wantedp(sname): return + if not series.wantedp: return season = series.ensure_season() if epi is None: epi = season.ep_i if ti == "-": check(season.implicitp, "audio source, but explicit season") - if not me._wantedp(sname): return - dir = lookup(me._audirs, sname, "no title, and no audio directory") + dir = lookup(me._audirs, series.name, + "no title, and no audio directory") src = lookup(dir.episodes, season.ep_i, "episode %d not found in audio dir `%s'" % (epi, dir.dir)) else: - try: src = me._isos[sname] - except KeyError: - check(season.i is not None, "must use explicit iso for movies") - dir = lookup(me._vdirs, sname, - "title, but no iso or video directory") - vseason = lookup(dir.seasons, season.i, - "season %d not found in video dir `%s'" % - (season.i, dir.dir)) - src = lookup(vseason.episodes, season.ep_i, - "episode %d.%d not found in video dir `%s'" % - (season.i, season.ep_i, dir.dir)) - - episode = season.add_episode(epi, neps, title, src, ti, ch) + try: src = me._isos[series.name] + except KeyError: src = me._auto_epsrc(series) + + episode = season.add_episode(epi, neps, title, src, ti, loch, hich) me._pl.add_episode(episode) me._cur_episode = episode @@ -598,7 +602,7 @@ class EpisodeListParser (object): def done(me): discs = set() for name, vdir in me._vdirs.items(): - if not me._wantedp(name): continue + if not me._series[name].wantedp: continue for s in vdir.seasons.values(): for d in s.episodes.values(): discs.add(d)