├── Grabby2.0.py └── README.md /Grabby2.0.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import re 4 | import subprocess 5 | import urllib2 6 | import json 7 | from bs4 import BeautifulSoup 8 | from signal import signal, SIGPIPE, SIG_DFL 9 | from zipfile import ZipFile 10 | 11 | def choice(maxep, llist, dlist, flist): 12 | 13 | media = ['Movie','TV'] 14 | language = ["English","Chinese","Arabian","French","Spanish","Danish","German","Vietnamese"] 15 | rangelist = xrange(1, int(maxep)+1) 16 | episodes = [('E' + str(s).zfill(2)) for s in rangelist] 17 | res = ["480p","720p","1080p","OTHER"] 18 | dictFilter = {} 19 | finallist = [] 20 | keyDict = {'media':media,'language':language,'episodes':episodes,'res':res} 21 | def Choicy(typezz): 22 | print 23 | if typezz == 'media': 24 | msg1 = 'Media Type > ' 25 | msg2 = 'Please select movie or tv only!' 26 | elif typezz == 'language': 27 | msg1 = 'Language > ' 28 | msg2 = 'Please select a right language!' 29 | elif typezz == 'episodes': 30 | msg1 = 'Episode > ' 31 | msg2 = 'Please select a correct episode number!' 32 | elif typezz == 'res': 33 | msg1 = 'Resolution > ' 34 | msg2 = 'Please select a correct resolution!' 35 | reflist = keyDict[str(typezz)] 36 | while True: 37 | countmedia = 0 38 | for x, i in enumerate(reflist): 39 | print x+1, i 40 | countmedia += 1 41 | dictFilter[typezz] = raw_input(msg1) 42 | try: 43 | val = int(dictFilter[typezz]) 44 | except ValueError: 45 | print "Please enter the correct selection!" 46 | if (val <= 0 or val > countmedia): 47 | print msg2 48 | else: 49 | final = val - 1 50 | dictFilter[typezz] = reflist[final] 51 | break 52 | for i in keyDict: 53 | Choicy(i) 54 | la = zip(llist,dlist,flist) 55 | for i in la: 56 | a, b, c = i 57 | if dictFilter['media'] == 'TV': 58 | if dictFilter['language'] in a: 59 | if dictFilter['res'] != 'OTHER': 60 | if dictFilter['episodes'] in b and dictFilter['res'] in b: 61 | finallist.append((b,c)) 62 | else: 63 | if dictFilter['episodes'] in b: 64 | finallist.append((b,c)) 65 | elif dictFilter['media'] == 'Movie': 66 | if dictFilter['language'] in a: 67 | if dictFilter['res'] != 'OTHER': 68 | if dictFilter['res'] in b: 69 | finallist.append((b,c)) 70 | else: 71 | finallist.append((b,c)) 72 | 73 | finaldict = {} 74 | print 75 | for i,x in enumerate(finallist): 76 | a, b = x 77 | c = i + 1 78 | finaldict[c] = b 79 | print c, a 80 | 81 | while True: 82 | choice = raw_input("Pick one: ") 83 | if not choice: 84 | print "Please enter something first!" 85 | continue 86 | try: 87 | val = int(choice) 88 | except ValueError: 89 | print "Please enter the correct selection!" 90 | if int(choice) < 1 or int(choice) > len(finallist): 91 | print "Please enter the correct choice!" 92 | else: 93 | print "Ok, downloading.... " 94 | baseurl = 'http://subscene.com/' 95 | url = baseurl + finaldict[int(choice)] 96 | subl = requests.get(url).text 97 | finalsoup = BeautifulSoup(subl,'html.parser') 98 | for link in finalsoup.find_all('a'): 99 | i = link.get('href') 100 | if '/subtitle/download' in i: 101 | finallink = baseurl + i 102 | basefile = 'sub' + '.zip' 103 | here = requests.get(finallink) 104 | g = open(basefile, 'wb') 105 | g.write(here.content) 106 | g.close() 107 | with ZipFile(basefile,'r') as zippy: 108 | zippy.extractall() 109 | zippy.close() 110 | print "All done!" 111 | os.remove('htmlr.html') 112 | os.remove('choice.json') 113 | os.remove(basefile) 114 | break 115 | 116 | def query(req): 117 | 118 | base_link = 'http://subscene.com/subtitles/title?q=' 119 | link = base_link + req 120 | htmll = requests.get(link).text 121 | soup = BeautifulSoup(htmll,'html.parser') 122 | links = [i.get('href') for i in [x for x in soup.find_all('a')]] 123 | print 'Which one again?' 124 | count = 1 125 | dic_count = {} 126 | for selection in links: 127 | if ('http' not in selection and len(selection) > 2 and 'legal-information' 128 | not in selection and '/site/contact' not in selection): 129 | print count, selection 130 | dic_count[count] = selection 131 | count += 1 132 | choiced = "" 133 | while True: 134 | choice = raw_input("Pick one: ") 135 | if not choice: 136 | print "Please enter something first!" 137 | continue 138 | try: 139 | val = int(choice) 140 | except ValueError: 141 | print "Please enter the correct selection!" 142 | if int(choice) < 1 or int(choice) > count: 143 | print "Please enter the correct choice!" 144 | else: 145 | print 'Hold on, doing some funky stuffs....' 146 | choiced = dic_count[int(choice)] 147 | break 148 | grabjson(choiced) 149 | while True: 150 | try: 151 | maxep, llist, dlist, flist = readjson() 152 | except Exception as timeout: 153 | continue 154 | break 155 | return (str(maxep), llist, dlist, flist) 156 | 157 | def grabjson(urlz): 158 | 159 | baseurl = 'http://www.subscene.com/' 160 | urll = baseurl+urlz 161 | headers = { 'User-Agent' : 'Mozilla/5.0' } 162 | req = urllib2.Request(urll, None, headers) 163 | file("htmlr.html", "w").write(urllib2.urlopen(req).read()) 164 | cmd = '''cat ./htmlr.html | pup 'tr td[class="a1"] a json{}' > choice.json ''' 165 | cmdd = subprocess.Popen(cmd,stdout=subprocess.PIPE,shell=True, preexec_fn = lambda: signal(SIGPIPE, SIG_DFL)) 166 | 167 | def readjson(): 168 | with open('choice.json') as datafile: 169 | js = json.load(datafile) 170 | 171 | i = 0 172 | llist = [] 173 | dlist = [] 174 | flist = [] 175 | while i < len(js): 176 | x = js[i] 177 | for xx in x: 178 | if xx == 'children': 179 | z = x[xx] 180 | a, b = z 181 | llist.append(a['text']) 182 | try: 183 | dlist.append(str(b['text'])) 184 | except Exception as e: 185 | dlist.append('Error') 186 | continue 187 | i+=1 188 | 189 | x = 0 190 | while x < len(js): 191 | c = js[x] 192 | for cc in c: 193 | if cc == 'href': 194 | flist.append(c[cc]) 195 | x += 1 196 | 197 | dmd = '''cat choice.json | grep -oE 'E[[:digit:]][[:digit:]]' | tr -d 'E' | sort -nr | head -n 1''' 198 | dmdd = subprocess.Popen(dmd,stdout=subprocess.PIPE,shell=True, preexec_fn = lambda: signal(SIGPIPE, SIG_DFL)) 199 | maxep = dmdd.stdout.read() 200 | if not maxep: 201 | maxep = 1 202 | return (str(maxep),llist,dlist,flist) 203 | 204 | def _main_(): 205 | reqMov = raw_input('What are you watching? : ') 206 | movie = "" 207 | for i in reqMov.split(): 208 | movie += (i + '+') 209 | maxep, llist, dlist, flist = query(movie) 210 | returndict = choice(str(maxep), llist, dlist, flist) 211 | _main_() 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | __GrabbySub2.0 - Grab your sub from the terminal, and grab the right one! :v: :collision: :rocket:__ 3 | 4 | _GrabbySub is a small little tool for you :cinema:/:tv: lovers out there who hates scrolling through all hundreds of choices just to find the right language, resolution and uploading version._ 5 | 6 | I technically live in the terminal, so I really find it painful to go through all the old school web browsing just to get my movie subtitle whenever I decide to relax. With that, I decided to write a little script to help me get my subtitle faster and also learn a thing or two from that. This is essentially just a learning tool for me, but if it improves my movie watching experience throughout the process, who cares?. 7 | 8 | The first version was written in Bash and so as handy as it was, it wasn't really that swift and user-friendly. Thus, GrabbySub2 is born, built on Python this time. The loading speed is greatly improved, with a much more pleasant user interacting interface. 9 | 10 | 11 | # Getting Started 12 | 13 | With this update, it truly transformed that sub grabbing process. You just call up the sub-servant, type in your movie/show, pick the specs and bam! You got yourself the right sub. Subscene has the right idea with the open-contribution community, which allows for precision. However, the precision should really come with ease. 14 | 15 | 16 | ## Prerequisites 17 | 18 | * `Python` - If you didn't have it already, you should download it. 19 | * `Pip` - The python module downloader 20 | * `Pup` - HTML parser for shell 21 | 22 | _Note: Current version works on OS X & Linux only. GUI coming soon!_ 23 | 24 | ## Installation 25 | 26 | We need to download a couple of stuffs first 27 | 28 | ### Install `Brew` (OS X users only) 29 | 30 | Open terminal and type: 31 | 32 | ```shell 33 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 34 | ``` 35 | 36 | ### Download `Python` & `Pip` 37 | 38 | #### OS X 39 | 40 | ```shell 41 | brew install python 42 | curl -s -L 'https://bootstrap.pypa.io/get-pip.py' -O 43 | python get-pip.py 44 | ``` 45 | 46 | #### Linux (Debian) 47 | 48 | ```shell 49 | apt-get install python -y 50 | curl -s -L 'https://bootstrap.pypa.io/get-pip.py' -O 51 | python get-pip.py 52 | ``` 53 | 54 | #### Linux (RPM) 55 | 56 | ```shell 57 | yum install python 58 | curl -s -L 'https://bootstrap.pypa.io/get-pip.py' -O 59 | python get-pip.py 60 | ``` 61 | 62 | ### Downloading additional modules: 63 | 64 | #### Make sure you run `pip` as root and download all of these modules: 65 | 66 | ```shell 67 | sudo pip install beautifulsoup4 zipfile re requests 68 | ``` 69 | 70 | #### Lastly, we need to install `pup` HTML-parser: 71 | 72 | If you have Go installed, use: 73 | ``` 74 | go get github.com/ericchiang/pup 75 | ``` 76 | 77 | For OS X users, use 78 | ``` 79 | brew install https://raw.githubusercontent.com/EricChiang/pup/master/pup.rb 80 | ``` 81 | 82 | To build from source, visit [pup's repo](https://github.com/EricChiang/pup/releases/tag/v0.3.9) and download your correct version. 83 | 84 | _Note: The packages differ from machine to machine. You might have already gotten them, some people haven\'t so it doesn't hurt to go through this process just to make sure it runs swiftly._ 85 | 86 | 87 | # Usage 88 | 89 | #### Creating an alias for ease of use: 90 | 91 | **Note:** 92 | ``` 93 | Due to some dependencies problem with PUP, it can get tricky if you use another shell other than bash. If this is the case, please use bash when you need to run Grabby. I will try to resolve this issue soon. 94 | ``` 95 | 96 | 97 | ```shell 98 | alias grabby='python ~/Downloads/Grabby2.0.py' 99 | ``` 100 | 101 | Open up terminal, go to your movie directory, and just run `grabby` 102 | 103 | #### Type in your `show | movie` & select the correct season/version 104 | 105 | ![selectmov](http://i.imgur.com/9B9zsxG.png) 106 | 107 | #### Select the `type` of media (TV/Mov) and the `Language` 108 | 109 | ![selectall](http://i.imgur.com/nSm4THG.png) 110 | 111 | #### Select your `episode` and `resolution`, then pick a link: 112 | 113 | ![selectres](http://i.imgur.com/Q2wr1zg.png) 114 | 115 | #### Et voila! :angel: :clap: 116 | 117 | ![download](http://i.imgur.com/ttYJOXP.png) 118 | 119 | 120 | # Adding Languages & Encoding Version 121 | 122 | Your language might not be included, or maybe you want to add some vendor specific stuffs like (HDTV, YIFY, DEMAND...) in the res section, don't worry, just do this: 123 | 124 | #### Adding Languages 125 | 126 | Just fire up your text editor, search for the pattern: `language = [` and add your language there, in quotes of course. 127 | 128 | E.g: 129 | 130 | ```python 131 | language = ["English","Chinese","Arabian","French","Spanish","Danish","German","Vietnamese","etc...."] 132 | ``` 133 | 134 | #### Adding resolution 135 | 136 | Do the same with language, but this time search for: `res = [` and add your resolution there. 137 | 138 | ```python 139 | res = ["480p","720p","1080p","HDTV","JYK","etc..."] 140 | ``` 141 | 142 | # Bugs & Improvements 143 | 144 | 145 | - [ ] Even faster :exclamation: The current version isn't so bad. Version 2.0 runs on python so it's almost double the speed of the first one which was as slow as a bash script can get 146 | 147 | - [ ] Completely eliminate dependencies by packaging pup as well as python modules along with the source. 148 | 149 | - [ ] Work on a GUI, integrate into a small handy taskbar/dock utility which improves users' experience... 150 | 151 | - [ ] ... Suggestions :question: 152 | 153 | # Contact me 154 | 155 | If you have any questions, shoot me a message at coffeeforthoughts@gmail.com 156 | 157 | As always, thanks for reading/using/checking out/frowning :joy_cat: :heart_eyes: :dizzy: :poop: 158 | 159 | __All done! Enjoy!__ 160 | --------------------------------------------------------------------------------