- (ep.label(), ch.title, ch.url))
-
-UNSET = ["UNSET"]
-
-def parse_list(fn):
- playlist = Playlist()
- season, episode, chapter, ep_i = None, None, None, 1
- vds = {}
- ads = iso = None
- with location(FileLocation(fn, 0)) as floc:
- with open(fn, "r") as f:
- for line in f:
- floc.stepline()
- sline = line.lstrip()
- if sline == "" or sline.startswith(";"): continue
-
- if line.startswith("!"):
- ww = Words(line[1:])
- cmd = ww.nextword()
- check(cmd is not None, "missing command")
-
- if cmd == "season":
- v = ww.nextword();
- check(v is not None, "missing season number")
- if v == "-":
- check(v.rest() is None, "trailing junk")
- season = playlist.add_movies()
- else:
- i = getint(v)
- title = ww.rest()
- season = playlist.add_season(title, i, implicitp = False)
- episode = chapter = None
- ep_i = 1
-
- elif cmd == "movie":
- check(ww.rest() is None, "trailing junk")
- season = playlist.add_movies()
- episode = chapter = None
- ep_i = 1
-
- elif cmd == "epname":
- name = ww.rest()
- check(name is not None, "missing episode name")
- try: sep = name.index(":")
- except ValueError: names = name + "s"
- else: name, names = name[:sep], name[sep + 1:]
- playlist.epname, playlist.epnames = name, names
-
- elif cmd == "epno":
- i = ww.rest()
- check(i is not None, "missing episode number")
- ep_i = getint(i)
-
- elif cmd == "iso":
- fn = ww.rest(); check(fn is not None, "missing filename")
- if fn == "-": iso = None
- else:
- check(OS.path.exists(OS.path.join(ROOT, fn)),
- "iso file `%s' not found" % fn)
- iso = VideoDisc(fn)
-
- elif cmd == "vdir":
- name = ww.nextword(); check(name is not None, "missing name")
- fn = ww.rest(); check(fn is not None, "missing directory")
- if fn == "-":
- try: del vds[name]
- except KeyError: pass
- else:
- vds[name] = VideoDir(fn)
-
- elif cmd == "adir":
- fn = ww.rest(); check(fn is not None, "missing directory")
- if fn == "-": ads = None
- else: ads = AudioDir(fn)
-
- elif cmd == "end":
- break
-
- else:
- raise ExpectedError("unknown command `%s'" % cmd)
-
- else:
+ (label, ch.title, ch.url))
+
+MODE_UNSET = 0
+MODE_SINGLE = 1
+MODE_MULTI = 2
+
+class EpisodeListParser (object):
+
+ def __init__(me, series_wanted = None, chapters_wanted_p = False):
+ me._pl = Playlist()
+ me._cur_episode = me._cur_chapter = None
+ me._series = {}; me._vdirs = {}; me._audirs = {}; me._isos = {}
+ me._series_wanted = series_wanted
+ me._chaptersp = chapters_wanted_p
+ if series_wanted is None: me._mode = MODE_UNSET
+ else: me._mode = MODE_MULTI
+
+ def _bad_keyval(me, cmd, k, v):
+ raise ExpectedError("invalid `!%s' option `%s'" %
+ (cmd, v if k is None else k))
+
+ def _keyvals(me, opts):
+ if opts is not None:
+ for kv in opts.split(","):
+ try: sep = kv.index("=")
+ except ValueError: yield None, kv
+ else: yield kv[:sep], kv[sep + 1:]
+
+ def _set_mode(me, mode):
+ if me._mode == MODE_UNSET:
+ me._mode = mode
+ elif me._mode != mode:
+ raise ExpectedError("inconsistent single-/multi-series usage")
+
+ def _get_series(me, name):
+ if name is None:
+ me._set_mode(MODE_SINGLE)
+ try: series = me._series[None]
+ except KeyError:
+ series = me._series[None] = Series(me._pl, None)
+ me._pl.nseries += 1
+ else:
+ me._set_mode(MODE_MULTI)
+ series = lookup(me._series, name, "unknown series `%s'" % name)
+ return series
+
+ def _opts_series(me, cmd, opts):
+ name = None
+ 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)
+
+ 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):
+
+ cmd = ww.nextword(); check(cmd is not None, "missing command")
+ try: sep = cmd.index(":")
+ except ValueError: opts = None
+ else: cmd, opts = cmd[:sep], cmd[sep + 1:]
+
+ if cmd == "series":
+ name = None
+ for k, v in me._keyvals(opts):
+ if k is None: name = v
+ else: me._bad_keyval(cmd, k, v)
+ check(name is not None, "missing series name")
+ 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 = 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 = me._opts_series(cmd, opts)
+ w = ww.nextword();
+ check(w is not None, "missing season number")
+ if w == "-":
+ if not series.wantedp: return
+ series.add_movies(ww.rest())
+ else:
+ title = ww.rest(); i = getint(w)
+ 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()
+
+ elif cmd == "epname":
+ for k, v in me._keyvals(opts): me._bad_keyval("epname", k, v)
+ name = ww.rest(); check(name is not None, "missing episode name")
+ try: sep = name.index(",")
+ except ValueError: names = name + "s"
+ else: name, names = name[:sep], name[sep + 1:]
+ me._pl.epname, me._pl.epnames = name, names
+
+ elif cmd == "epno":
+ series = me._opts_series(cmd, opts)
+ w = ww.rest(); check(w is not None, "missing episode number")
+ epi = getint(w)
+ if not series.wantedp: return
+ series.ensure_season().ep_i = epi
+
+ elif cmd == "iso":
+ series = me._opts_series(cmd, opts)
+ fn = ww.rest(); check(fn is not None, "missing filename")
+ 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[series.name] = VideoDisc(fn)
+
+ elif cmd == "vdir":
+ series = me._opts_series(cmd, opts)
+ dir = ww.rest(); check(dir is not None, "missing directory")
+ if not series.wantedp: return
+ if dir == "-": forget(me._vdirs, series.name)
+ else: me._vdirs[series.name] = VideoDir(dir)
+
+ elif cmd == "adir":
+ series = me._opts_series(cmd, opts)
+ dir = ww.rest(); check(dir is not None, "missing directory")
+ if not series.wantedp: return
+ if dir == "-": forget(me._audirs, series.name)
+ else: me._audirs[series.name] = AudioDir(dir)
+
+ elif cmd == "displaced":
+ series = me._opts_series(cmd, opts)
+ w = ww.rest(); check(w is not None, "missing count"); n = getint(w)
+ src = me._auto_epsrc(series)
+ src.nuses += n
+ else:
+ raise ExpectedError("unknown command `%s'" % cmd)
+
+ def _process_episode(me, ww):
+
+ opts = ww.nextword(); check(opts is not None, "missing title/options")
+ 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)
+ elif v == "-": ti = "-"
+ else: sname = v
+ elif k == "s": sname = v
+ elif k == "n": neps = getint(v)
+ elif k == "ep": epi = 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 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")
+ 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))