PageRenderTime 46ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/marathon.py

https://github.com/svip/Marathon
Python | 445 lines | 440 code | 3 blank | 2 comment | 6 complexity | 05fa03852a2f587b25021b327cca2d43 MD5 | raw file
  1. #!/usr/bin/env python
  2. # -*- encoding: utf-8 -*-
  3. from basic import Basic
  4. import re, os, sys, pickle, time, urllib
  5. from config import Config
  6. class Marathon(Basic):
  7. c = None
  8. rss = None
  9. shows = []
  10. currentshow = ''
  11. knownfileextensions = ["mpg", "avi", "mkv", "mp4"]
  12. MAIN, SHOW, SHOWS, ADDSHOWS, RSS, SETTINGS = range(6)
  13. def get_show_count(self):
  14. return len(self.shows)
  15. def is_video(self, f):
  16. w = f.split(".")
  17. return w[len(w)-1] in self.knownfileextensions
  18. def analyse_filename(self, f, backupseason):
  19. try:
  20. t = re.search("s?([0-9][0-9]?) ?[xe]([0-9][0-9]?)", f, re.I)
  21. season = int(t.group(1))
  22. number = int(t.group(2))
  23. except AttributeError:
  24. print "File `%s' confused me..." % f
  25. try:
  26. if backupseason == None:
  27. season = int(self.i("Which season is this? "))
  28. else:
  29. season = backupseason
  30. number = int(self.i("Which number in the season is this? "))
  31. except ValueError:
  32. return self.analyse_filename(f, backupseason)
  33. print "Assuming season %s, episode %s for `%s'..." % (season, number, f)
  34. return (f, season, number)
  35. def obtain_episodes(self, episodes, top, d, backupseason):
  36. for r, d, files in os.walk(top + "/" + d):
  37. for f in files:
  38. if self.is_video(f):
  39. (filename, season, number) = self.analyse_filename(f, backupseason)
  40. try:
  41. t = episodes[season]
  42. except:
  43. episodes.update({season : {}})
  44. episodes[season].update({number : os.path.join(r, filename)})
  45. return episodes
  46. def season_guess(self, d):
  47. t = re.search("(season|s) ?([0-9][0-9]?)", d, re.I)
  48. try:
  49. season = int(t.group(2))
  50. except (ValueError, AttributeError):
  51. return None
  52. return season
  53. def known_episode(self, show, filename):
  54. for season in show['episodes']:
  55. for episode in show['episodes'][season]:
  56. if filename in show['episodes'][season][episode]:
  57. return True
  58. return False
  59. def add_files(self, title, show, ignoreknown=False):
  60. print "Where should I look for files of it?"
  61. path = self.i("Path: ")
  62. searchtitle = title.lower().replace(" ", "[ \._]")
  63. for r, dirs, files in os.walk(path):
  64. for d in dirs:
  65. if re.match(searchtitle, d, re.I):
  66. print "Found `%s'..." % d
  67. p = self.i("Should I include this directory for this show? (Y/n) ")
  68. if p == "" or p.lower() == "y":
  69. seasonguess = self.season_guess(d)
  70. s = self.i("Which season? (0 for more than one%s) " % (", blank for season %s" % seasonguess if seasonguess != None else ""))
  71. if s == "0":
  72. s = None
  73. elif s == "":
  74. if seasonguess == None:
  75. s = None
  76. else:
  77. s = seasonguess
  78. show['episodes'] = self.obtain_episodes(show['episodes'], path, d, s)
  79. print ""
  80. for f in files:
  81. if re.match(searchtitle, f, re.I):
  82. if ignoreknown and self.known_episode(show, f):
  83. print "Ignoring `%s' as I know it..." % f
  84. else:
  85. print "Found file `%s'..." % f
  86. p = self.i("Should I include this file for this show? (Y/n) ")
  87. if p == "" or p.lower() == "y":
  88. (filename, season, number) = self.analyse_filename(f, None)
  89. try:
  90. show['episodes'][season].update({number : {'file': os.path.join(r, filename), 'mplayersettings': ''}})
  91. except:
  92. show['episodes'].update({season : {number: {'file': os.path.join(r, filename), 'mplayersettings': ''}}})
  93. print ""
  94. break
  95. return show
  96. def add_show(self):
  97. print "Time to configure a new show..."
  98. show = {'episodes' : {}, 'mplayersettings': ''}
  99. title = self.i("Title: ")
  100. show.update({'title' : title, 'currentepisode' : (1, 1)})
  101. show = self.add_files(title, show)
  102. while True:
  103. if self.yesno("Should we include another directory?", self.NO):
  104. show = self.add_files(title, show)
  105. else:
  106. break
  107. self.c.showdata.update({title : show})
  108. if not title in self.shows:
  109. self.shows.append(title)
  110. def mplayer(self, filename, mplayersettings=None):
  111. if mplayersettings==None:
  112. s = os.system("mplayer %s \"%s\"" % (self.c.mplayerconfig, filename))
  113. else:
  114. s = os.system("mplayer %s \"%s\"" % (mplayersettings, filename))
  115. return s == 0
  116. def watch(self, curep):
  117. ep = self.c.showdata[self.currentshow]['episodes'][curep[0]][curep[1]]
  118. try:
  119. s = self.c.showdata[self.currentshow]['mplayersettings']
  120. if s.strip() == '':
  121. s = None
  122. except KeyError:
  123. s = None
  124. if self.mplayer(ep, s):
  125. self.c.showdata[self.currentshow]['currentepisode'] = self.next_episode(self.c.showdata[self.currentshow]['episodes'], curep)
  126. return True
  127. else:
  128. return False
  129. def cwatch(self, curep):
  130. while True:
  131. if self.watch(curep):
  132. curep = self.c.showdata[self.currentshow]['currentepisode']
  133. else:
  134. break
  135. def get_episodes_left(self, showdata):
  136. try:
  137. curep = showdata['currentepisode']
  138. except:
  139. curep = (1,1)
  140. c = 0
  141. for season in showdata['episodes']:
  142. if season < curep[0]:
  143. continue
  144. for episode in showdata['episodes'][season]:
  145. if season == curep[0] and episode <= curep[1]:
  146. continue
  147. c += 1
  148. return c
  149. def seasonsort(self, x, y):
  150. return x[0] - y[0]
  151. def handle_episode(self, show, season, episode):
  152. print ""
  153. print "Episode: (S%sE%s)\n%s" % (self.z(season), self.z(episode), self.c.showdata[show]['episodes'][season][episode])
  154. p = self.menu([("WATCH", "Watch"),
  155. ("CURRENT", "Set this to current"),
  156. ("EDIT", "Edit"),
  157. ("REMOVE", "Remove")], None)
  158. if p == "RETURN":
  159. return
  160. else:
  161. if p == "WATCH":
  162. pass
  163. elif p == "CURRENT":
  164. self.c.showdata[show]['currentepisode'] = (season, episode)
  165. print "Current episode to season %s, episode %s." % (season, episode)
  166. elif p == "EDIT":
  167. pass
  168. elif p == "REMOVE":
  169. self.c.showdata[show]['episodes'][season].pop(episode)
  170. print "Removed."
  171. self.handle_episode(show, season, episode)
  172. def browse_season(self, show, season):
  173. print ""
  174. print "Episodes of season %s:" % season
  175. tmp = []
  176. for episode in self.c.showdata[show]['episodes'][season]:
  177. t = self.c.showdata[show]['episodes'][season][episode]
  178. try:
  179. t = t.split("/")
  180. except AttributeError:
  181. print "Unusual error"
  182. print t,dir(t)
  183. return
  184. tmp.append((episode, t[len(t)-1]))
  185. tmp.sort(self.seasonsort)
  186. for episode in tmp:
  187. print "%s: %s" % episode
  188. p = self.i("Option: ", True)
  189. if p == "RETURN":
  190. return
  191. else:
  192. try:
  193. e = int(p)
  194. t = self.c.showdata[show]['episodes'][season][e]
  195. self.handle_episode(show, season, e)
  196. except (ValueError, KeyError):
  197. pass
  198. self.browse_season(show, season)
  199. def browse_show(self, show):
  200. print ""
  201. print "Known seasons:"
  202. tmp = []
  203. topop = []
  204. for season in self.c.showdata[show]['episodes']:
  205. try:
  206. int(season)
  207. tmp.append((season, len(self.c.showdata[show]['episodes'][season])))
  208. except ValueError:
  209. topop.append(season)
  210. for p in topop:
  211. self.c.showdata[show]['episodes'].pop(p)
  212. tmp.sort(self.seasonsort)
  213. for season in tmp:
  214. print "%s: (%s episodes)" % season
  215. p = self.i("Option: ", True)
  216. if p == "RETURN":
  217. return
  218. else:
  219. try:
  220. s = int(p)
  221. t = self.c.showdata[show]['episodes'][s]
  222. self.browse_season(show, s)
  223. except (ValueError, KeyError, TypeError):
  224. pass
  225. self.browse_show(show)
  226. def set_currentshow(self, s):
  227. self.currentshow = s
  228. try:
  229. self.c.showdata[0]['currentshow'] = self.currentshow
  230. except KeyError:
  231. try:
  232. self.c.showdata[0].update({'currentshow' : self.currentshow})
  233. except KeyError:
  234. self.c.showdata.update({0 : {'currentshow' : self.currentshow}})
  235. def download_torrent(self, url, title):
  236. os.system("wget \"%s\" -O \"%s/%s.torrent\"" % (url, self.c.torrentwatchdir, title.replace(' ', '_')))
  237. def rssmenu(self):
  238. if len(self.rssfeeds) < 1:
  239. t = self.i("You have no feeds set, want to set one now? (Y/n)" )
  240. if t != 'y' and t != '':
  241. return
  242. if self.torrentwatchdir == '':
  243. directory = self.i("Please type the directory you want the torrents to be saved in (no trailing /):")
  244. self.torrentwatchdir = directory
  245. feed = self.i("Please type in the RSS feed URL:")
  246. self.rssfeeds = [feed]
  247. self.rss.update_feeds(self.rssfeeds)
  248. items = self.rss.get_torrents()
  249. if len(items) < 1:
  250. print "No new RSS items."
  251. else:
  252. fullbreak = False
  253. for item in items:
  254. print item['title']
  255. action = ''
  256. while True:
  257. action = self.i("Download? (Y/n) ")
  258. if action == None:
  259. fullbreak = True
  260. break
  261. if action == '':
  262. action = 'y'
  263. action = action.lower()
  264. if not action in ['y', 'n']:
  265. print "Not understood."
  266. else:
  267. break
  268. if fullbreak:
  269. break
  270. if action == 'y':
  271. self.download_torrent(item['url'], item['title'])
  272. self.c.rsslastupdate = self.rss.rssfeeds[0]['lastupdate']
  273. def run(self):
  274. # this will eventually be more fluid and just a hackish
  275. # run loop like this.
  276. menu = self.MAIN
  277. while True:
  278. # main loop
  279. print ""
  280. if menu == self.MAIN:
  281. print "You have %s show(s) in your configuration." % len(self.shows)
  282. print "Please select an option:"
  283. p = self.menu([("SHOW", "Select a show to watch%s" % (" (disabled)" if len(self.shows)==0 else "")),
  284. ("ADD", "Add shows"),
  285. ("RSS", "Check RSS feeds"),
  286. ("SETTINGS", "Settings")], "SHOW")
  287. if p == "SHOW":
  288. if len(self.shows) == 0:
  289. print "No shows available, please add some."
  290. else:
  291. menu = self.SHOWS
  292. elif p == "ADD":
  293. menu = self.ADDSHOWS
  294. elif p == "RSS":
  295. menu = self.RSS
  296. elif p == "SETTINGS":
  297. menu = self.SETTINGS
  298. elif menu == self.SHOWS:
  299. print "Select show:"
  300. i = 0
  301. for show in self.shows:
  302. i += 1
  303. print "%s: %s (%s left)" % (i, show, self.get_episodes_left(self.c.showdata[show]))
  304. p = self.i("Option: ", True)
  305. if p == "RETURN":
  306. menu = self.MAIN
  307. else:
  308. try:
  309. s = self.shows[int(p)-1]
  310. self.set_currentshow(s)
  311. menu = self.SHOW
  312. except IndexError:
  313. print "Ununderstood option."
  314. continue
  315. except ValueError:
  316. continue
  317. except TypeError:
  318. continue
  319. elif menu == self.SHOW:
  320. print "Selected `%s'..." % self.currentshow
  321. try:
  322. curep = self.c.showdata[self.currentshow]['currentepisode']
  323. except:
  324. self.c.showdata[self.currentshow]['currentepisode'] = (1, 1)
  325. curep = (1, 1)
  326. try:
  327. tep = self.c.showdata[self.currentshow]['episodes'][curep[0]][curep[1]]
  328. except KeyError:
  329. while True:
  330. curep = (curep[0], curep[1]-1)
  331. if curep[1] < 1:
  332. curep = (curep[0]-1, 1)
  333. if curep[0] < 1:
  334. tep = None
  335. break
  336. try:
  337. tep = self.c.showdata[self.currentshow]['episodes'][curep[0]][curep[1]]
  338. break
  339. except KeyError:
  340. continue
  341. if tep != None:
  342. t = tep.split("/")
  343. t = t[len(t)-1]
  344. print "Current episode: S%sE%s: %s" % (self.z(curep[0]), self.z(curep[1]), t)
  345. p = self.menu([("WATCH", "Watch current episode"),
  346. ("CWATCH", "Continuesly watch without prompt (crash mplayer to stop)"),
  347. ("ADD", "Add more/new episodes"),
  348. ("BROWSE", "Browse show's files"),
  349. ("MPLAYER", "MPlayer settings")], "WATCH")
  350. if p == "WATCH":
  351. self.watch(curep)
  352. elif p == "CWATCH":
  353. self.cwatch(curep)
  354. elif p == "ADD":
  355. self.c.showdata[self.currentshow] = self.add_files(self.currentshow, self.c.showdata[self.currentshow], True)
  356. elif p == "BROWSE":
  357. self.browse_show(self.currentshow)
  358. elif p == "MPLAYER":
  359. try:
  360. settings = self.c.showdata[self.currentshow]['mplayersettings']
  361. except:
  362. settings = ''
  363. self.c.showdata[self.currentshow].update({'mplayersettings':''})
  364. print ""
  365. print "Set mplayer settings for `%s':" % self.c.showdata[self.currentshow]['title']
  366. print settings
  367. print "Hit enter to make no chance; space and enter to clear."
  368. p = self.i(": ")
  369. if p != '':
  370. self.c.showdata[self.currentshow]['mplayersettings'] = p
  371. elif p == "RETURN":
  372. menu = self.SHOWS
  373. elif p == "TOP":
  374. menu = self.MAIN
  375. else:
  376. menu = self.SHOWS
  377. elif menu == self.ADDSHOWS:
  378. self.add_show()
  379. menu = self.MAIN
  380. elif menu == self.RSS:
  381. self.rssmenu()
  382. menu = self.MAIN
  383. elif menu == self.SETTINGS:
  384. print "Select settings to edit."
  385. p = self.menu([("MPLAYER", "mplayer options")], None)
  386. if p == "MPLAYER":
  387. print "Please write on one line, the options to pass to mplayer:"
  388. print "Current options: %s" % self.mplayerconfig
  389. print "Hit enter to make no chance; space and enter to clear."
  390. c = self.i(": ")
  391. if c != '':
  392. self.c.mplayerconfig = c
  393. self.c.showdata[0]['mplayerconfig'] = c
  394. print "Set mplayer option to: %s" % c
  395. elif p == "RETURN":
  396. menu = self.MAIN
  397. def next_episode(self, episodes, current):
  398. if current[0] > len(episodes):
  399. try:
  400. return (current[0]-1, len(episodes[current[1]])-1)
  401. except KeyError:
  402. return (1,1)
  403. try:
  404. t = episodes[current[0]][current[1]+1]
  405. return (current[0], current[1]+1)
  406. except:
  407. return self.next_episode(episodes, (current[0]+1, 0))
  408. def z(self, i):
  409. if i < 10:
  410. return "0%s" % i
  411. return i
  412. def save(self):
  413. self.c.save()
  414. def __init__(self):
  415. self.c = Config()