Skip to content

Instantly share code, notes, and snippets.

@philipbjorge
Created January 26, 2013 04:03
Show Gist options
  • Save philipbjorge/4640105 to your computer and use it in GitHub Desktop.
Save philipbjorge/4640105 to your computer and use it in GitHub Desktop.
goatee - Minimal sickbeard replacement using nzbx.co for data Requires the following python modules: guessit fileDownloader.py
__author__ = 'philipbjorge'
import sys, urllib, json, argparse, urllib2
from os import remove
from os.path import abspath, isdir, join, exists, getsize
from guessit import guess_episode_info
from collections import defaultdict
from sys import stderr
import fileDownloader
DEBUG = True
def main():
def ui(s):
print s
def dbg(s):
if DEBUG:
ui(s)
def err(s):
print >> stderr, s
parser = argparse.ArgumentParser(description='Minimal alternative to Sickbeard.')
parser.add_argument("-l", "--location", help="where the nzbs will be downloaded to", required=False, default=".")
parser.add_argument("query", help="the tv show to search nzbx.co for")
args = parser.parse_args()
if not isdir(abspath(args.location)):
print abspath(args.location) + " is not a valid download directory"
return 1
ui(abspath(args.location) + " where I'll be saving nzbs")
url = "https://nzbx.co/api/search?q=" + urllib.quote_plus(args.query)
try:
result = json.load(urllib.urlopen(url))
except Exception:
err("Error connecting connecting to nzbx or reading it's data")
exit()
"""
{
name: "Sid.The.Science.Kid.E132.Special.Sunny.Dad.Day.WS.DSR.XviD-WaLMaRT",
spam: 0,
fromname: "teevee@4u.net (teevee)",
size: 297497475,
groupid: 4,
categoryid: 5030,
totalpart: 29,
completion: 100,
rageid: "-1",
imdbid: "",
comments: "0",
guid: "b961cd22eb47939133b03515ddf644b2",
adddate: 1355650196,
postdate: 1254843914,
downloads: "0",
votes: {
upvotes: 0,
downvotes: 0
},
nzb: "https://nzbx.co/nzb?b961cd22eb47939133b03515ddf644b2*|*Sid.The.Science.Kid.E132.Special.Sunny.Dad.Day.WS.DSR.XviD-WaLMaRT"
}
"""
# Construct a 2 level tree
# First level = season
# Second level = episode -- this node contains a list of all matching nzbs
episode_bucket_tree = defaultdict(lambda : defaultdict(list))
for nzb in result:
filename, category = nzb["name"], nzb["categoryid"]
# filters bucket to include only TV category
if 5000 <= category and category <= 5999:
nzb_episode_info = guess_episode_info(filename)
# Filter for only very complete metadata
if "season" in nzb_episode_info and "episodeNumber" in nzb_episode_info\
and "series" in nzb_episode_info and nzb_episode_info["series"].strip().lower() == args.query.strip().lower():
nzb_episode_info["nzb"] = nzb["nzb"]
episode_bucket_tree[nzb_episode_info["season"]][nzb_episode_info["episodeNumber"]].append(nzb_episode_info)
id_to_nzb = defaultdict(list)
n = 0
nzb_list_output = ""
for season, episodes in sorted(episode_bucket_tree.items()):
if isinstance(season, (int, long)) and season > 0:
nzb_list_output += "Season %d\n" % season
id_to_nzb["s%d" % season] = list()
for episode, nzbs in sorted(episodes.items()):
if isinstance(episode, (int, long)) and episode > 0:
nzb_list_output += "%d x\t\t" % len(nzbs)
nzb_list_output += "%sS%dE%d\t\tid=%d\n" % (args.query, nzbs[0]["season"], nzbs[0]["episodeNumber"], n)
id_to_nzb[n] = nzbs
id_to_nzb["s%d" % season].append(nzbs)
n += 1
print nzb_list_output
need_more_shows = True
show_or_ep = ""
while need_more_shows:
ui("Type q to quit")
while show_or_ep.lower() != "s" and show_or_ep.lower() != "e":
show_or_ep = raw_input("Season or Episode? [s/e]: ")
if show_or_ep.lower() == "q":
exit()
if show_or_ep.lower() == "s":
# SEASON DL
nos = raw_input("Season no(s) seperated by spaces to download: ")
if "q" in nos.lower():
exit()
ids = map(int, nos.split())
for i in ids:
try:
for eps in id_to_nzb["s%d" % i]:
# multiple episode nzb options
for ep in eps:
ui("Downloading %s" % ep["nzb"])
fname = "%sS%dE%d.nzb" % (args.query, ep["season"], ep["episodeNumber"])
fname = join(abspath(args.location), fname)
try:
dl = fileDownloader.DownloadFile(ep["nzb"], join(abspath(args.location), fname), timeout=20.0, autoretry=True, retries=3)
dl.download()
except urllib2.URLError:
pass
if exists(fname):
break
if not exists(fname) or getsize(fname) < 1:
if exists(fname):
remove(fname)
err("No nzb was downloaded for %s" % fname)
except Exception as e:
print e
err("invalid season %d" % i)
else:
# EPISODE DL
ids = map(int, raw_input("ID(s) seperated by spaces to download (right column): ").split())
for i in ids:
try:
# multiple episode nzb options
ui("Downloading %s" % id_to_nzb[i][0]["nzb"])
for nzb in id_to_nzb[i]:
fname = "%sS%dE%d.nzb" % (args.query, nzb["season"], nzb["episodeNumber"])
fname = join(abspath(args.location), fname)
try:
dl = fileDownloader.DownloadFile(nzb["nzb"], join(abspath(args.location), fname), timeout=20.0, autoretry=True, retries=3)
dl.download()
except urllib2.URLError:
pass
if exists(fname):
break
if not exists(fname) or getsize(fname) < 1:
if exists(fname):
remove(fname)
err("No nzb was downloaded for %s" % fname)
except Exception as e:
err("invalid id %d" % i)
print nzb_list_output
show_or_ep = ""
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment