Skip to content

Commit

Permalink
v0.5.17
Browse files Browse the repository at this point in the history
  • Loading branch information
rembo10 committed Nov 10, 2016
2 parents 5a2082e + 579b5a2 commit 0a805a0
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 81 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## v0.5.17
Released 10 November 2016

Highlights:
* Added: t411 support
* Fixed: Rutracker login
* Fixed: Deluge empty password
* Fixed: FreeBSD init script
* Improved: Musicbrainz searching

The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.16...v0.5.17).

## v0.5.16
Released 10 June 2016

Expand Down
11 changes: 10 additions & 1 deletion Headphones.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def main():
help='Prevent browser from launching on startup')
parser.add_argument(
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
parser.add_argument(
'--host', help='Specify a host (default - localhost)')

args = parser.parse_args()

Expand Down Expand Up @@ -170,6 +172,13 @@ def main():
else:
http_port = int(headphones.CONFIG.HTTP_PORT)

# Force the http host if neccessary
if args.host:
http_host = args.host
logger.info('Using forced web server host: %s', http_host)
else:
http_host = headphones.CONFIG.HTTP_HOST

# Check if pyOpenSSL is installed. It is required for certificate generation
# and for CherryPy.
if headphones.CONFIG.ENABLE_HTTPS:
Expand All @@ -183,7 +192,7 @@ def main():
# Try to start the server. Will exit here is address is already in use.
web_config = {
'http_port': http_port,
'http_host': headphones.CONFIG.HTTP_HOST,
'http_host': http_host,
'http_root': headphones.CONFIG.HTTP_ROOT,
'http_proxy': headphones.CONFIG.HTTP_PROXY,
'enable_https': headphones.CONFIG.ENABLE_HTTPS,
Expand Down
17 changes: 17 additions & 0 deletions data/interfaces/default/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,22 @@ <h1 class="clearfix"><i class="fa fa-gear"></i> Settings</h1>
</div>
</div>
</fieldset>

<fieldset>
<div class="row checkbox left">
<input id="use_tquattrecentonze" type="checkbox" class="bigcheck" name="use_tquattrecentonze" value="1" ${config['use_tquattrecentonze']} /><label for="use_tquattrecentonze"><span class="option">t411</span></label>
</div>
<div class="config">
<div class="row">
<label>Username</label>
<input type="text" name="tquattrecentonze_user" value="${config['tquattrecentonze_user']}" size="36">
</div>
<div class="row">
<label>Password</label>
<input type="password" name="tquattrecentonze_password" value="${config['tquattrecentonze_password']}" size="36">
</div>
</div>
</fieldset>

</fieldset>
</td>
Expand Down Expand Up @@ -2400,6 +2416,7 @@ <h1 class="clearfix"><i class="fa fa-gear"></i> Settings</h1>
initConfigCheckbox("#api_enabled");
initConfigCheckbox("#enable_https");
initConfigCheckbox("#customauth");
initConfigCheckbox("#use_tquattrecentonze");


$('#twitterStep1').click(function () {
Expand Down
3 changes: 3 additions & 0 deletions headphones/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ def __repr__(self):
'TWITTER_PASSWORD': (str, 'Twitter', ''),
'TWITTER_PREFIX': (str, 'Twitter', 'Headphones'),
'TWITTER_USERNAME': (str, 'Twitter', ''),
'TQUATTRECENTONZE': (int, 'tquattrecentonze', 0),
'TQUATTRECENTONZE_PASSWORD': (str, 'tquattrecentonze', ''),
'TQUATTRECENTONZE_USER': (str, 'tquattrecentonze', ''),
'UPDATE_DB_INTERVAL': (int, 'General', 24),
'USENET_RETENTION': (int, 'General', '1500'),
'UTORRENT_HOST': (str, 'uTorrent', ''),
Expand Down
36 changes: 36 additions & 0 deletions headphones/crier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pprint
import sys
import threading
import traceback

from headphones import logger


def cry():
"""
Logs thread traces.
"""
tmap = {}
main_thread = None
# get a map of threads by their ID so we can print their names
# during the traceback dump
for t in threading.enumerate():
if t.ident:
tmap[t.ident] = t
else:
main_thread = t

# Loop over each thread's current frame, writing info about it
for tid, frame in sys._current_frames().iteritems():
thread = tmap.get(tid, main_thread)

lines = []
lines.append('%s\n' % thread.getName())
lines.append('========================================\n')
lines += traceback.format_stack(frame)
lines.append('========================================\n')
lines.append('LOCAL VARIABLES:\n')
lines.append('========================================\n')
lines.append(pprint.pformat(frame.f_locals))
lines.append('\n\n')
logger.info("".join(lines))
3 changes: 2 additions & 1 deletion headphones/deluge.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ def _get_auth():
delugeweb_host = headphones.CONFIG.DELUGE_HOST
delugeweb_cert = headphones.CONFIG.DELUGE_CERT
delugeweb_password = headphones.CONFIG.DELUGE_PASSWORD
logger.debug('Deluge: Using password %s******%s' % (delugeweb_password[0], delugeweb_password[-1]))
if len(delugeweb_password) > 0:
logger.debug('Deluge: Using password %s******%s' % (delugeweb_password[0], delugeweb_password[-1]))

if not delugeweb_host.startswith('http'):
delugeweb_host = 'http://%s' % delugeweb_host
Expand Down
1 change: 1 addition & 0 deletions headphones/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ def replace_illegal_chars(string, type="file"):
# Translation table.
# Cover additional special characters processing normalization.
u"'": '', # replace apostrophe with nothing
u"’": '', # replace musicbrainz style apostrophe with nothing
u'&': ' and ', # expand & to ' and '
}

Expand Down
26 changes: 4 additions & 22 deletions headphones/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,6 @@ def findArtist(name, limit=1):
artistlist = []
artistResults = None

chars = set('!?*-')
if any((c in chars) for c in name):
name = '"' + name + '"'

criteria = {'artist': name.lower()}

with mb_lock:
Expand Down Expand Up @@ -156,16 +152,13 @@ def findRelease(name, limit=1, artist=None):
if not artist and ':' in name:
name, artist = name.rsplit(":", 1)

chars = set('!?*-')
if any((c in chars) for c in name):
name = '"' + name + '"'
if artist and any((c in chars) for c in artist):
artist = '"' + artist + '"'
criteria = {'release': name.lower()}
if artist:
criteria['artist'] = artist.lower()

with mb_lock:
try:
releaseResults = musicbrainzngs.search_releases(query=name, limit=limit, artist=artist)[
'release-list']
releaseResults = musicbrainzngs.search_releases(limit=limit, **criteria)['release-list']
except musicbrainzngs.WebServiceError as e: # need to update exceptions
logger.warn('Attempt to query MusicBrainz for "%s" failed: %s' % (name, str(e)))
mb_lock.snooze(5)
Expand Down Expand Up @@ -234,10 +227,6 @@ def findSeries(name, limit=1):
serieslist = []
seriesResults = None

chars = set('!?*-')
if any((c in chars) for c in name):
name = '"' + name + '"'

criteria = {'series': name.lower()}

with mb_lock:
Expand Down Expand Up @@ -759,19 +748,12 @@ def findArtistbyAlbum(name):

def findAlbumID(artist=None, album=None):
results = None
chars = set('!?*-')

try:
if album and artist:
if any((c in chars) for c in album):
album = '"' + album + '"'
if any((c in chars) for c in artist):
artist = '"' + artist + '"'
criteria = {'release': album.lower()}
criteria['artist'] = artist.lower()
else:
if any((c in chars) for c in album):
album = '"' + album + '"'
criteria = {'release': album.lower()}
with mb_lock:
results = musicbrainzngs.search_release_groups(limit=1, **criteria).get(
Expand Down
28 changes: 15 additions & 13 deletions headphones/rutracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,30 @@ def login(self):
logger.info("Attempting to log in to rutracker...")

try:
r = self.session.post(loginpage, data=post_params, timeout=self.timeout)
r = self.session.post(loginpage, data=post_params, timeout=self.timeout, allow_redirects=False)
# try again
if 'bb_data' not in r.cookies.keys():
if not self.has_bb_data_cookie(r):
time.sleep(10)
r = self.session.post(loginpage, data=post_params, timeout=self.timeout)
if r.status_code != 200:
logger.error("rutracker login returned status code %s" % r.status_code)
self.loggedin = False
r = self.session.post(loginpage, data=post_params, timeout=self.timeout, allow_redirects=False)
if self.has_bb_data_cookie(r):
self.loggedin = True
logger.info("Successfully logged in to rutracker")
else:
if 'bb_data' in r.cookies.keys():
self.loggedin = True
logger.info("Successfully logged in to rutracker")
else:
logger.error(
"Could not login to rutracker, credentials maybe incorrect, site is down or too many attempts. Try again later")
self.loggedin = False
logger.error(
"Could not login to rutracker, credentials maybe incorrect, site is down or too many attempts. Try again later")
self.loggedin = False
return self.loggedin
except Exception as e:
logger.error("Unknown error logging in to rutracker: %s" % e)
self.loggedin = False
return self.loggedin

def has_bb_data_cookie(self, response):
if 'bb_data' in response.cookies.keys():
return True
# Rutracker randomly send a 302 redirect code, cookie may be present in response history
return next(('bb_data' in r.cookies.keys() for r in response.history), False)

def searchurl(self, artist, album, year, format):
"""
Return the search url
Expand Down
86 changes: 86 additions & 0 deletions headphones/searcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.

# NZBGet support added by CurlyMo <[email protected]> as a part of XBian - XBMC on the Raspberry Pi
# t411 support added by a1ex, @likeitneverwentaway on github for maintenance

from base64 import b16encode, b32decode
from hashlib import sha1
Expand All @@ -24,6 +25,7 @@
import subprocess
import unicodedata
import urlparse
from json import loads

import os
import re
Expand Down Expand Up @@ -813,6 +815,19 @@ def send_to_downloader(data, bestqual, album):
torrent_name = helpers.replace_illegal_chars(folder_name) + '.torrent'
download_path = os.path.join(headphones.CONFIG.TORRENTBLACKHOLE_DIR, torrent_name)

# Blackhole for t411
if bestqual[2].lower().startswith("http://api.t411"):
if headphones.CONFIG.MAGNET_LINKS == 2:
try:
url = bestqual[2].split('TOKEN')[0]
token = bestqual[2].split('TOKEN')[1]
data = request.request_content(url, headers={'Authorization': token})
torrent_to_file(download_path, data)
logger.info('Successfully converted magnet to torrent file')
except Exception as e:
logger.error("Error converting magnet link: %s" % str(e))
return

if bestqual[2].lower().startswith("magnet:"):
if headphones.CONFIG.MAGNET_LINKS == 1:
try:
Expand Down Expand Up @@ -1763,6 +1778,77 @@ def set_proxy(proxy_url):
resultlist.append((title, size, url, provider, 'torrent', match))
except Exception as e:
logger.exception("Unhandled exception in Mininova Parser")
# t411
if headphones.CONFIG.TQUATTRECENTONZE:
username = headphones.CONFIG.TQUATTRECENTONZE_USER
password = headphones.CONFIG.TQUATTRECENTONZE_PASSWORD
API_URL = "http://api.t411.ch"
AUTH_URL = API_URL + '/auth'
DL_URL = API_URL + '/torrents/download/'
provider = "t411"
t411_term = term.replace(" ", "%20")
SEARCH_URL = API_URL + '/torrents/search/' + t411_term + "?limit=15&cid=395&subcat=623"
headers_login = {'username': username, 'password': password}

# Requesting content
logger.info('Parsing results from t411 using search term: %s' % term)
req = request.request_content(AUTH_URL, method='post', data=headers_login)

if len(req.split('"')) == 9:
token = req.split('"')[7]
headers_auth = {'Authorization': token}
logger.info('t411 - User %s logged in' % username)
else:
logger.info('t411 - Login error : %s' % req.split('"')[3])

# Quality
if headphones.CONFIG.PREFERRED_QUALITY == 3 or losslessOnly:
providerurl = fix_url(SEARCH_URL + "&term[16][]=529&term[16][]=1184")
elif headphones.CONFIG.PREFERRED_QUALITY == 1 or allow_lossless:
providerurl = fix_url(SEARCH_URL + "&term[16][]=685&term[16][]=527&term[16][]=1070&term[16][]=528&term[16][]=1167&term[16][]=1166&term[16][]=530&term[16][]=529&term[16][]=1184&term[16][]=532&term[16][]=533&term[16][]=1085&term[16][]=534&term[16][]=535&term[16][]=1069&term[16][]=537&term[16][]=538")
elif headphones.CONFIG.PREFERRED_QUALITY == 0:
providerurl = fix_url(SEARCH_URL + "&term[16][]=685&term[16][]=527&term[16][]=1070&term[16][]=528&term[16][]=1167&term[16][]=1166&term[16][]=530&term[16][]=532&term[16][]=533&term[16][]=1085&term[16][]=534&term[16][]=535&term[16][]=1069&term[16][]=537&term[16][]=538")
else:
providerurl = fix_url(SEARCH_URL)

# Tracker search
req = request.request_content(providerurl, headers=headers_auth)
req = loads(req)
total = req['total']

# Process feed
if total == '0':
logger.info("No results found from t411 for %s" % term)
else:
logger.info('Found %s results from t411' % total)
torrents = req['torrents']
for torrent in torrents:
try:
title = torrent['name']
if torrent['seeders'] < minimumseeders:
logger.info('Skipping torrent %s : seeders below minimum set' % title)
continue
id = torrent['id']
size = int(torrent['size'])
data = request.request_content(DL_URL + id, headers=headers_auth)

# Blackhole
if headphones.CONFIG.TORRENT_DOWNLOADER == 0 and headphones.CONFIG.MAGNET_LINKS == 2:
url = DL_URL + id + 'TOKEN' + token
resultlist.append((title, size, url, provider, 'torrent', True))

# Build magnet
else:
metadata = bdecode(data)
hashcontents = bencode(metadata['info'])
digest = sha1(hashcontents).hexdigest()
trackers = [metadata["announce"]][0]
url = 'magnet:?xt=urn:btih:%s&tr=%s' % (digest, trackers)
resultlist.append((title, size, url, provider, 'torrent', True))

except Exception as e:
logger.error("Error converting magnet link: %s" % str(e))
return

# attempt to verify that this isn't a substring result
# when looking for "Foo - Foo" we don't want "Foobar"
Expand Down
Loading

0 comments on commit 0a805a0

Please sign in to comment.