mirror of
https://github.com/rembo10/headphones.git
synced 2026-09-10 00:32:52 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b71c0d8b93 | ||
|
|
0ecfe499fc | ||
|
|
68d85841ea | ||
|
|
1ffcc177a4 | ||
|
|
2230c5bc0c | ||
|
|
352b009e91 | ||
|
|
b6b33e1b1e | ||
|
|
dff1ac2e41 | ||
|
|
92601e0eb8 | ||
|
|
6e82423947 | ||
|
|
0a805a01f3 | ||
|
|
579b5a2f2f | ||
|
|
c77f43be5e | ||
|
|
f10d1834dd | ||
|
|
b4c7ed2227 | ||
|
|
0086980b1b | ||
|
|
89e1793978 | ||
|
|
5a598fe611 | ||
|
|
b58f670694 | ||
|
|
5103436d17 | ||
|
|
c9a1796e56 | ||
|
|
7428b56dd5 | ||
|
|
42294d261d | ||
|
|
31a99a908a | ||
|
|
aa503f0fe8 | ||
|
|
d3d48d27df | ||
|
|
bd8eb74662 | ||
|
|
99680e9a0f | ||
|
|
0cc58bdb46 | ||
|
|
09435b8786 | ||
|
|
5d2753a34e | ||
|
|
8eb3ece2f9 | ||
|
|
d3584a5ae9 | ||
|
|
c0c64621e6 | ||
|
|
323d364572 | ||
|
|
9a681a9422 | ||
|
|
a7a709df51 | ||
|
|
3f23b5553a |
@@ -1,5 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
## v0.5.18
|
||||
Released 01 December 2016
|
||||
|
||||
Highlights:
|
||||
* Added: PassTheHeadphones support
|
||||
* Fixed: Special characters in password fields breaking on config page
|
||||
* Improved: Updated t411 url
|
||||
|
||||
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.17...v0.5.18).
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
+10
-1
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
@@ -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,
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<label title="Password for web server authentication. Leave empty to disable.">
|
||||
HTTP Password
|
||||
</label>
|
||||
<input type="password" name="http_password" value="${config['http_password']}" size="30">
|
||||
<input type="password" name="http_password" value="${config['http_password'] | h}" size="30">
|
||||
</div>
|
||||
<div class="row checkbox">
|
||||
<input type="checkbox" name="launch_browser" value="1" ${config['launch_browser']} />
|
||||
@@ -173,7 +173,7 @@
|
||||
<label title="SABnzbd password. Leave empty if not applicable.">
|
||||
SABnzbd Password
|
||||
</label>
|
||||
<input type="password" name="sab_password" value="${config['sab_password']}" size="20">
|
||||
<input type="password" name="sab_password" value="${config['sab_password'] | h}" size="20">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label title="SABnzbd API key. Can be found in SABnzbd settings.">
|
||||
@@ -207,7 +207,7 @@
|
||||
<label title="NZBGet password. Leave empty if not applicable">
|
||||
NZBget Password
|
||||
</label>
|
||||
<input type="password" name="nzbget_password" value="${config['nzbget_password']}" size="20">
|
||||
<input type="password" name="nzbget_password" value="${config['nzbget_password'] | h}" size="20">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label title="Name of NZBget category to add downloads to.">
|
||||
@@ -359,7 +359,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Transmission Password</label>
|
||||
<input type="password" name="transmission_password" value="${config['transmission_password']}" size="30">
|
||||
<input type="password" name="transmission_password" value="${config['transmission_password'] | h}" size="30">
|
||||
</div>
|
||||
<div class="row">
|
||||
<small>Note: With Transmission, you can specify a different download directory for downloads sent from Headphones.
|
||||
@@ -379,7 +379,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>uTorrent Password</label>
|
||||
<input type="password" name="utorrent_password" value="${config['utorrent_password']}" size="30">
|
||||
<input type="password" name="utorrent_password" value="${config['utorrent_password'] | h}" size="30">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>uTorrent Label</label>
|
||||
@@ -400,7 +400,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Deluge Password</label>
|
||||
<input type="password" name="deluge_password" value="${config['deluge_password']}" size="30">
|
||||
<input type="password" name="deluge_password" value="${config['deluge_password'] | h}" size="30">
|
||||
</div>
|
||||
<div class="row">
|
||||
<small>Note: With Deluge, you can specify a different download directory for downloads sent from Headphones.
|
||||
@@ -466,7 +466,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label>
|
||||
<input class="hppass" type="password" value="${config['hppass']}" size="20">
|
||||
<input class="hppass" type="password" value="${config['hppass'] | h}" size="20">
|
||||
</div>
|
||||
<div class="row">
|
||||
<a href="https://headphones.codeshy.com/vip" id="vipserver" target="_blank">Don't have an account? Sign up!</a>
|
||||
@@ -622,7 +622,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label>
|
||||
<input type="password" name="rutracker_password" value="${config['rutracker_password']}" size="36">
|
||||
<input type="password" name="rutracker_password" value="${config['rutracker_password'] | h}" size="36">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Seed Ratio</label>
|
||||
@@ -642,7 +642,11 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label>
|
||||
<input type="password" name="whatcd_password" value="${config['whatcd_password']}" size="36">
|
||||
<input type="password" name="whatcd_password" value="${config['whatcd_password'] | h}" size="36">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>URL</label>
|
||||
<input type="text" name="whatcd_url" value="${config['whatcd_url']}" size="36">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Seed Ratio</label>
|
||||
@@ -651,6 +655,30 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="row checkbox left">
|
||||
<input id="use_pth" type="checkbox" class="bigcheck" name="use_pth" value="1" ${config['use_pth']} /><label for="use_pth"><span class="option">PassTheHeadphones.me</span></label>
|
||||
</div>
|
||||
<div class="config">
|
||||
<div class="row">
|
||||
<label>Username</label>
|
||||
<input type="text" name="pth_username" value="${config['pth_username']}" size="36">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label>
|
||||
<input type="password" name="pth_password" value="${config['pth_password']}" size="36">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>URL</label>
|
||||
<input type="text" name="pth_url" value="${config['pth_url']}" size="36">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Seed Ratio</label>
|
||||
<input type="text" class="override-float" name="pth_ratio" value="${config['pth_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<div class="row checkbox left">
|
||||
<input id="use_strike" type="checkbox" class="bigcheck" name="use_strike" value="1" ${config['use_strike']} /><label for="use_strike"><span class="option">Strike Search</span></label>
|
||||
@@ -743,6 +771,22 @@
|
||||
</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'] | h}" size="36">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</fieldset>
|
||||
</td>
|
||||
@@ -964,7 +1008,7 @@
|
||||
<label>SMTP User</label><input type="text" name="email_smtp_user" value="${config['email_smtp_user']}" size="254">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>SMTP Password</label><input type="password" name="email_smtp_password" value="${config['email_smtp_password']}" size="50">
|
||||
<label>SMTP Password</label><input type="password" name="email_smtp_password" value="${config['email_smtp_password'] | h}" size="50">
|
||||
</div>
|
||||
<div class="row checkbox">
|
||||
<input type="text" class="override-float" name="email_smtp_port" value="${config['email_smtp_port']}" size="4"><label>SMTP Port</label>
|
||||
@@ -990,7 +1034,7 @@
|
||||
<label>Growl Host:Port</label><input type="text" name="growl_host" value="${config['growl_host']}" size="30">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Growl Password</label><input type="password" name="growl_password" value="${config['growl_password']}" size="30">
|
||||
<label>Growl Password</label><input type="password" name="growl_password" value="${config['growl_password'] | h}" size="30">
|
||||
</div>
|
||||
<div class="row checkbox">
|
||||
<input type="checkbox" name="growl_onsnatch" value="1" ${config['growl_onsnatch']} /><label>Notify on snatch?</label>
|
||||
@@ -1012,7 +1056,7 @@
|
||||
<label>Username</label><input type="text" name="xbmc_username" value="${config['xbmc_username']}" size="30">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label><input type="password" name="xbmc_password" value="${config['xbmc_password']}" size="30">
|
||||
<label>Password</label><input type="password" name="xbmc_password" value="${config['xbmc_password'] | h}" size="30">
|
||||
</div>
|
||||
<div class="checkbox row">
|
||||
<input type="checkbox" name="xbmc_update" value="1" ${config['xbmc_update']} /><label>Update Library</label>
|
||||
@@ -1126,7 +1170,7 @@
|
||||
<small>Username of your Plex client API (blank for none)</small>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Plex Password</label><input type="password" name="plex_password" value="${config['plex_password']}" size="30">
|
||||
<label>Plex Password</label><input type="password" name="plex_password" value="${config['plex_password'] | h}" size="30">
|
||||
<small>Password of your Plex client API (blank for none)</small>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -1226,7 +1270,7 @@
|
||||
<label>Subsonic Username</label><input type="text" name="subsonic_username" value="${config['subsonic_username']}" size="30">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Subsonic Password</label><input type="password" name="subsonic_password" value="${config['subsonic_password']}" size="30">
|
||||
<label>Subsonic Password</label><input type="password" name="subsonic_password" value="${config['subsonic_password'] | h}" size="30">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -1626,7 +1670,7 @@
|
||||
<label>Username</label><input type="text" class="customuser" name="customuser" value="${config['customuser']}" size="20">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label><input type="password" class="custompass" name="custompass" value="${config['custompass']}" size="15"><br>
|
||||
<label>Password</label><input type="password" class="custompass" name="custompass" value="${config['custompass'] | h}" size="15"><br>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -1639,7 +1683,7 @@
|
||||
<label>Username</label><input type="text" class="hpuser" name="hpuser" value="${config['hpuser']}" size="20">
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label><input type="password" class="hppass" name="hppass" value="${config['hppass']}" size="20"><br>
|
||||
<label>Password</label><input type="password" class="hppass" name="hppass" value="${config['hppass'] | h}" size="20"><br>
|
||||
<a href="https://headphones.codeshy.com/vip" id="vipserver" target="_blank">Get an Account!</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2396,10 +2440,12 @@
|
||||
initConfigCheckbox("#use_waffles");
|
||||
initConfigCheckbox("#use_rutracker");
|
||||
initConfigCheckbox("#use_whatcd");
|
||||
initConfigCheckbox("#use_pth");
|
||||
initConfigCheckbox("#use_strike");
|
||||
initConfigCheckbox("#api_enabled");
|
||||
initConfigCheckbox("#enable_https");
|
||||
initConfigCheckbox("#customauth");
|
||||
initConfigCheckbox("#use_tquattrecentonze");
|
||||
|
||||
|
||||
$('#twitterStep1').click(function () {
|
||||
|
||||
@@ -274,6 +274,9 @@ _CONFIG_DEFINITIONS = {
|
||||
'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', ''),
|
||||
@@ -290,6 +293,12 @@ _CONFIG_DEFINITIONS = {
|
||||
'WHATCD_PASSWORD': (str, 'What.cd', ''),
|
||||
'WHATCD_RATIO': (str, 'What.cd', ''),
|
||||
'WHATCD_USERNAME': (str, 'What.cd', ''),
|
||||
'WHATCD_URL': (str, 'What.cd', 'https://what.cd'),
|
||||
'PTH': (int, 'PassTheHeadphones.me', 0),
|
||||
'PTH_PASSWORD': (str, 'PassTheHeadphones.me', ''),
|
||||
'PTH_RATIO': (str, 'PassTheHeadphones.me', ''),
|
||||
'PTH_USERNAME': (str, 'PassTheHeadphones.me', ''),
|
||||
'PTH_URL': (str, 'PassTheHeadphones.me', 'https://passtheheadphones.me'),
|
||||
'XBMC_ENABLED': (int, 'XBMC', 0),
|
||||
'XBMC_HOST': (str, 'XBMC', ''),
|
||||
'XBMC_NOTIFY': (int, 'XBMC', 0),
|
||||
|
||||
@@ -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))
|
||||
@@ -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
|
||||
|
||||
@@ -251,6 +251,7 @@ _XLATE_SPECIAL = {
|
||||
# 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 '
|
||||
}
|
||||
|
||||
|
||||
+4
-22
@@ -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:
|
||||
@@ -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)
|
||||
@@ -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:
|
||||
@@ -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(
|
||||
|
||||
+15
-13
@@ -44,28 +44,30 @@ class Rutracker(object):
|
||||
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
|
||||
|
||||
+218
-8
@@ -14,6 +14,7 @@
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# NZBGet support added by CurlyMo <curlymoo1@gmail.com> 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
|
||||
@@ -24,6 +25,7 @@ import datetime
|
||||
import subprocess
|
||||
import unicodedata
|
||||
import urlparse
|
||||
from json import loads
|
||||
|
||||
import os
|
||||
import re
|
||||
@@ -36,7 +38,6 @@ from headphones import logger, db, helpers, classes, sab, nzbget, request
|
||||
from headphones import utorrent, transmission, notifiers, rutracker, deluge
|
||||
from bencode import bencode, bdecode
|
||||
|
||||
|
||||
# Magnet to torrent services, for Black hole. Stolen from CouchPotato.
|
||||
TORRENT_TO_MAGNET_SERVICES = [
|
||||
# 'https://zoink.it/torrent/%s.torrent',
|
||||
@@ -161,6 +162,8 @@ def get_seed_ratio(provider):
|
||||
seed_ratio = headphones.CONFIG.KAT_RATIO
|
||||
elif provider == 'What.cd':
|
||||
seed_ratio = headphones.CONFIG.WHATCD_RATIO
|
||||
elif provider == 'PassTheHeadphones.Me':
|
||||
seed_ratio = headphones.CONFIG.PTH_RATIO
|
||||
elif provider == 'The Pirate Bay':
|
||||
seed_ratio = headphones.CONFIG.PIRATEBAY_RATIO
|
||||
elif provider == 'Old Pirate Bay':
|
||||
@@ -199,14 +202,13 @@ def searchforalbum(albumid=None, new=False, losslessOnly=False,
|
||||
continue
|
||||
|
||||
if headphones.CONFIG.WAIT_UNTIL_RELEASE_DATE and album['ReleaseDate']:
|
||||
try:
|
||||
release_date = datetime.datetime.strptime(album['ReleaseDate'], "%Y-%m-%d")
|
||||
except:
|
||||
logger.warn(
|
||||
"No valid date for: %s. Skipping automatic search" % album['AlbumTitle'])
|
||||
release_date = strptime_musicbrainz(album['ReleaseDate'])
|
||||
if not release_date:
|
||||
logger.warn("No valid date for: %s. Skipping automatic search" %
|
||||
album['AlbumTitle'])
|
||||
continue
|
||||
|
||||
if release_date > datetime.datetime.today():
|
||||
elif release_date > datetime.datetime.today():
|
||||
logger.info("Skipping: %s. Waiting for release date of: %s" % (
|
||||
album['AlbumTitle'], album['ReleaseDate']))
|
||||
continue
|
||||
@@ -235,6 +237,26 @@ def searchforalbum(albumid=None, new=False, losslessOnly=False,
|
||||
logger.info('Search for wanted albums complete')
|
||||
|
||||
|
||||
def strptime_musicbrainz(date_str):
|
||||
"""
|
||||
Release date as returned by Musicbrainz may contain the full date (Year-Month-Day)
|
||||
but it may as well be just Year-Month or even just the year.
|
||||
|
||||
Args:
|
||||
date_str: the date as a string (ex: "2003-05-01", "2003-03", "2003")
|
||||
|
||||
Returns:
|
||||
The more accurate datetime object we can create or None if parse failed
|
||||
"""
|
||||
acceptable_formats = ('%Y-%m-%d', '%Y-%m', '%Y')
|
||||
for date_format in acceptable_formats:
|
||||
try:
|
||||
return datetime.datetime.strptime(date_str, date_format)
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def do_sorted_search(album, new, losslessOnly, choose_specific_download=False):
|
||||
NZB_PROVIDERS = (headphones.CONFIG.HEADPHONES_INDEXER or
|
||||
headphones.CONFIG.NEWZNAB or
|
||||
@@ -253,6 +275,7 @@ def do_sorted_search(album, new, losslessOnly, choose_specific_download=False):
|
||||
headphones.CONFIG.WAFFLES or
|
||||
headphones.CONFIG.RUTRACKER or
|
||||
headphones.CONFIG.WHATCD or
|
||||
headphones.CONFIG.PTH or
|
||||
headphones.CONFIG.STRIKE)
|
||||
|
||||
results = []
|
||||
@@ -813,6 +836,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:
|
||||
@@ -1470,7 +1506,8 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None,
|
||||
try:
|
||||
logger.info(u"Attempting to log in to What.cd...")
|
||||
gazelle = gazelleapi.GazelleAPI(headphones.CONFIG.WHATCD_USERNAME,
|
||||
headphones.CONFIG.WHATCD_PASSWORD)
|
||||
headphones.CONFIG.WHATCD_PASSWORD,
|
||||
headphones.CONFIG.WHATCD_URL)
|
||||
gazelle._login()
|
||||
except Exception as e:
|
||||
gazelle = None
|
||||
@@ -1530,6 +1567,106 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None,
|
||||
provider,
|
||||
'torrent', True))
|
||||
|
||||
# PassTheHeadphones.me - Using same logic as What.CD as it's also Gazelle, so should really make this into something reusable
|
||||
if headphones.CONFIG.PTH:
|
||||
provider = "PassTheHeadphones.me"
|
||||
providerurl = "https://passtheheadphones.me/"
|
||||
|
||||
bitrate = None
|
||||
bitrate_string = bitrate
|
||||
|
||||
if headphones.CONFIG.PREFERRED_QUALITY == 3 or losslessOnly: # Lossless Only mode
|
||||
search_formats = [gazelleformat.FLAC]
|
||||
maxsize = 10000000000
|
||||
elif headphones.CONFIG.PREFERRED_QUALITY == 2: # Preferred quality mode
|
||||
search_formats = [None] # should return all
|
||||
bitrate = headphones.CONFIG.PREFERRED_BITRATE
|
||||
if bitrate:
|
||||
if 225 <= int(bitrate) < 256:
|
||||
bitrate = 'V0'
|
||||
elif 200 <= int(bitrate) < 225:
|
||||
bitrate = 'V1'
|
||||
elif 175 <= int(bitrate) < 200:
|
||||
bitrate = 'V2'
|
||||
for encoding_string in gazelleencoding.ALL_ENCODINGS:
|
||||
if re.search(bitrate, encoding_string, flags=re.I):
|
||||
bitrate_string = encoding_string
|
||||
if bitrate_string not in gazelleencoding.ALL_ENCODINGS:
|
||||
logger.info(
|
||||
u"Your preferred bitrate is not one of the available What.cd filters, so not using it as a search parameter.")
|
||||
maxsize = 10000000000
|
||||
elif headphones.CONFIG.PREFERRED_QUALITY == 1 or allow_lossless: # Highest quality including lossless
|
||||
search_formats = [gazelleformat.FLAC, gazelleformat.MP3]
|
||||
maxsize = 10000000000
|
||||
else: # Highest quality excluding lossless
|
||||
search_formats = [gazelleformat.MP3]
|
||||
maxsize = 300000000
|
||||
|
||||
if not gazelle or not gazelle.logged_in():
|
||||
try:
|
||||
logger.info(u"Attempting to log in to PassTheHeadphones.me...")
|
||||
gazelle = gazelleapi.GazelleAPI(headphones.CONFIG.PTH_USERNAME,
|
||||
headphones.CONFIG.PTH_PASSWORD,
|
||||
headphones.CONFIG.PTH_URL)
|
||||
gazelle._login()
|
||||
except Exception as e:
|
||||
gazelle = None
|
||||
logger.error(u"PassTheHeadphones credentials incorrect or site is down. Error: %s %s" % (
|
||||
e.__class__.__name__, str(e)))
|
||||
|
||||
if gazelle and gazelle.logged_in():
|
||||
logger.info(u"Searching %s..." % provider)
|
||||
all_torrents = []
|
||||
for search_format in search_formats:
|
||||
if usersearchterm:
|
||||
all_torrents.extend(
|
||||
gazelle.search_torrents(searchstr=usersearchterm, format=search_format,
|
||||
encoding=bitrate_string)['results'])
|
||||
else:
|
||||
all_torrents.extend(gazelle.search_torrents(artistname=semi_clean_artist_term,
|
||||
groupname=semi_clean_album_term,
|
||||
format=search_format,
|
||||
encoding=bitrate_string)['results'])
|
||||
|
||||
# filter on format, size, and num seeders
|
||||
logger.info(u"Filtering torrents by format, maximum size, and minimum seeders...")
|
||||
match_torrents = [t for t in all_torrents if
|
||||
t.size <= maxsize and t.seeders >= minimumseeders]
|
||||
|
||||
logger.info(
|
||||
u"Remaining torrents: %s" % ", ".join(repr(torrent) for torrent in match_torrents))
|
||||
|
||||
# sort by times d/l'd
|
||||
if not len(match_torrents):
|
||||
logger.info(u"No results found from %s for %s after filtering" % (provider, term))
|
||||
elif len(match_torrents) > 1:
|
||||
logger.info(u"Found %d matching releases from %s for %s - %s after filtering" %
|
||||
(len(match_torrents), provider, artistterm, albumterm))
|
||||
logger.info(
|
||||
"Sorting torrents by times snatched and preferred bitrate %s..." % bitrate_string)
|
||||
match_torrents.sort(key=lambda x: int(x.snatched), reverse=True)
|
||||
if gazelleformat.MP3 in search_formats:
|
||||
# sort by size after rounding to nearest 10MB...hacky, but will favor highest quality
|
||||
match_torrents.sort(key=lambda x: int(10 * round(x.size / 1024. / 1024. / 10.)),
|
||||
reverse=True)
|
||||
if search_formats and None not in search_formats:
|
||||
match_torrents.sort(
|
||||
key=lambda x: int(search_formats.index(x.format))) # prefer lossless
|
||||
# if bitrate:
|
||||
# match_torrents.sort(key=lambda x: re.match("mp3", x.getTorrentDetails(), flags=re.I), reverse=True)
|
||||
# match_torrents.sort(key=lambda x: str(bitrate) in x.getTorrentFolderName(), reverse=True)
|
||||
logger.info(
|
||||
u"New order: %s" % ", ".join(repr(torrent) for torrent in match_torrents))
|
||||
|
||||
for torrent in match_torrents:
|
||||
if not torrent.file_path:
|
||||
torrent.group.update_group_data() # will load the file_path for the individual torrents
|
||||
resultlist.append((torrent.file_path,
|
||||
torrent.size,
|
||||
gazelle.generate_torrent_link(torrent.id),
|
||||
provider,
|
||||
'torrent', True))
|
||||
|
||||
# Pirate Bay
|
||||
if headphones.CONFIG.PIRATEBAY:
|
||||
provider = "The Pirate Bay"
|
||||
@@ -1763,6 +1900,77 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None,
|
||||
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.li"
|
||||
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"
|
||||
@@ -1802,6 +2010,8 @@ def preprocess(resultlist):
|
||||
headers['User-Agent'] = USER_AGENT
|
||||
elif result[3] == 'What.cd':
|
||||
headers['User-Agent'] = 'Headphones'
|
||||
elif result[3] == 'PassTheHeadphones.me':
|
||||
headers['User-Agent'] = 'Headphones'
|
||||
elif result[3] == "The Pirate Bay" or result[3] == "Old Pirate Bay":
|
||||
headers[
|
||||
'User-Agent'] = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2243.2 Safari/537.36'
|
||||
|
||||
+17
-2
@@ -14,6 +14,7 @@
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# NZBGet support added by CurlyMo <curlymoo1@gmail.com> as a part of XBian - XBMC on the Raspberry Pi
|
||||
# t411 support added by a1ex, @likeitneverwentaway on github for maintenance
|
||||
|
||||
from operator import itemgetter
|
||||
import threading
|
||||
@@ -28,7 +29,7 @@ import urllib2
|
||||
|
||||
import os
|
||||
import re
|
||||
from headphones import logger, searcher, db, importer, mb, lastfm, librarysync, helpers, notifiers
|
||||
from headphones import logger, searcher, db, importer, mb, lastfm, librarysync, helpers, notifiers, crier
|
||||
from headphones.helpers import checked, radio, today, clean_name
|
||||
from mako.lookup import TemplateLookup
|
||||
from mako import exceptions
|
||||
@@ -69,6 +70,11 @@ class WebInterface(object):
|
||||
artists = myDB.select('SELECT * from artists order by ArtistSortName COLLATE NOCASE')
|
||||
return serve_template(templatename="index.html", title="Home", artists=artists)
|
||||
|
||||
@cherrypy.expose
|
||||
def threads(self):
|
||||
crier.cry()
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
|
||||
@cherrypy.expose
|
||||
def artistPage(self, ArtistID):
|
||||
myDB = db.DBConnection()
|
||||
@@ -1222,8 +1228,17 @@ class WebInterface(object):
|
||||
"whatcd_username": headphones.CONFIG.WHATCD_USERNAME,
|
||||
"whatcd_password": headphones.CONFIG.WHATCD_PASSWORD,
|
||||
"whatcd_ratio": headphones.CONFIG.WHATCD_RATIO,
|
||||
"whatcd_url": headphones.CONFIG.WHATCD_URL,
|
||||
"use_pth": checked(headphones.CONFIG.PTH),
|
||||
"pth_username": headphones.CONFIG.PTH_USERNAME,
|
||||
"pth_password": headphones.CONFIG.PTH_PASSWORD,
|
||||
"pth_ratio": headphones.CONFIG.PTH_RATIO,
|
||||
"pth_url": headphones.CONFIG.PTH_URL,
|
||||
"use_strike": checked(headphones.CONFIG.STRIKE),
|
||||
"strike_ratio": headphones.CONFIG.STRIKE_RATIO,
|
||||
"use_tquattrecentonze": checked(headphones.CONFIG.TQUATTRECENTONZE),
|
||||
"tquattrecentonze_user": headphones.CONFIG.TQUATTRECENTONZE_USER,
|
||||
"tquattrecentonze_password": headphones.CONFIG.TQUATTRECENTONZE_PASSWORD,
|
||||
"pref_qual_0": radio(headphones.CONFIG.PREFERRED_QUALITY, 0),
|
||||
"pref_qual_1": radio(headphones.CONFIG.PREFERRED_QUALITY, 1),
|
||||
"pref_qual_2": radio(headphones.CONFIG.PREFERRED_QUALITY, 2),
|
||||
@@ -1420,7 +1435,7 @@ class WebInterface(object):
|
||||
"use_newznab", "newznab_enabled", "use_torznab", "torznab_enabled",
|
||||
"use_nzbsorg", "use_omgwtfnzbs", "use_kat", "use_piratebay", "use_oldpiratebay",
|
||||
"use_mininova", "use_waffles", "use_rutracker",
|
||||
"use_whatcd", "use_strike", "preferred_bitrate_allow_lossless", "detect_bitrate",
|
||||
"use_whatcd", "use_strike", "use_tquattrecentonze", "preferred_bitrate_allow_lossless", "detect_bitrate",
|
||||
"ignore_clean_releases", "freeze_db", "cue_split", "move_files",
|
||||
"rename_files", "correct_metadata", "cleanup_files", "keep_nfo", "add_album_art",
|
||||
"embed_album_art", "embed_lyrics",
|
||||
|
||||
+16
-37
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# PROVIDE: headphones
|
||||
# REQUIRE: DAEMON sabnzbd
|
||||
# REQUIRE: DAEMON
|
||||
# BEFORE: LOGIN
|
||||
# KEYWORD: shutdown
|
||||
#
|
||||
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
|
||||
@@ -15,56 +16,34 @@
|
||||
# as root.
|
||||
# headphones_dir: Directory where Headphones lives.
|
||||
# Default: /usr/local/headphones
|
||||
# headphones_chdir: Change to this directory before running Headphones.
|
||||
# Default is same as headphones_dir.
|
||||
# headphones_pid: The name of the pidfile to create.
|
||||
# Default is headphones.pid in headphones_dir.
|
||||
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="headphones"
|
||||
rcvar=${name}_enable
|
||||
|
||||
load_rc_config ${name}
|
||||
|
||||
: "${headphones_enable:="NO"}"
|
||||
: "${headphones_user:="_sabnzbd"}"
|
||||
: "${headphones_dir:="/usr/local/headphones"}"
|
||||
: "${headphones_chdir:="${headphones_dir}"}"
|
||||
: "${headphones_pid:="${headphones_dir}/headphones.pid"}"
|
||||
: "${headphones_conf:="/usr/local/headphones/config.ini"}"
|
||||
|
||||
status_cmd="${name}_status"
|
||||
stop_cmd="${name}_stop"
|
||||
command="${headphones_dir}/Headphones.py"
|
||||
command_interpreter="/usr/bin/python"
|
||||
pidfile="/var/run/headphones/headphones.pid"
|
||||
start_precmd="headphones_start_precmd"
|
||||
headphones_flags="--daemon --nolaunch --pidfile $pidfile --config $headphones_conf $headphones_flags"
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="-f -p ${headphones_pid} python ${headphones_dir}/Headphones.py ${headphones_flags} --quiet --nolaunch"
|
||||
headphones_start_precmd() {
|
||||
if [ $($ID -u) != 0 ]; then
|
||||
err 1 "Must be root."
|
||||
fi
|
||||
|
||||
# Ensure user is root when running this script.
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "Oops, you should be root before running this!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_headphones_pid() {
|
||||
# Make sure the pid corresponds to the Headphones process.
|
||||
pid=$(cat "${headphones_pid}" 2>/dev/null)
|
||||
pgrep -F "${headphones_pid}" -q "python ${headphones_dir}/Headphones.py"
|
||||
return $?
|
||||
}
|
||||
|
||||
# Try to stop Headphones cleanly by calling shutdown over http.
|
||||
headphones_stop() {
|
||||
echo "Stopping $name"
|
||||
verify_headphones_pid
|
||||
if [ -n "${pid}" ]; then
|
||||
wait_for_pids "${pid}"
|
||||
echo "Stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
headphones_status() {
|
||||
verify_headphones_pid && echo "$name is running as ${pid}" || echo "$name is not running"
|
||||
if [ ! -d /var/run/headphones ]; then
|
||||
install -do $headphones_user /var/run/headphones
|
||||
fi
|
||||
}
|
||||
|
||||
load_rc_config ${name}
|
||||
run_rc_command "$1"
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
## PYTHON_BIN= #$DAEMON, the location of the python binary, the default is /usr/bin/python
|
||||
## HP_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for headphones, i.e. " --config=/home/headphones/config.ini"
|
||||
## HP_PORT= #$PORT_OPTS, hardcoded port for the webserver, overrides value in config.ini
|
||||
## HP_HOST= #$HOST_OPTS, host for the webserver, overrides value in config.ini
|
||||
##
|
||||
## EXAMPLE if want to run as different user
|
||||
## add HP_USER=username to /etc/default/headphones
|
||||
@@ -105,7 +106,12 @@ load_settings() {
|
||||
PORT_OPTS=" --port=${HP_PORT} "
|
||||
}
|
||||
|
||||
DAEMON_OPTS=" Headphones.py --quiet --daemon --nolaunch --pidfile=${PID_FILE} --datadir=${DATA_DIR} ${PORT_OPTS}${EXTRA_DAEMON_OPTS}"
|
||||
# Host config
|
||||
[ -n "$HP_HOST" ] && {
|
||||
HOST_OPTS=" --host=${HP_HOST} "
|
||||
}
|
||||
|
||||
DAEMON_OPTS=" Headphones.py --quiet --daemon --nolaunch --pidfile=${PID_FILE} --datadir=${DATA_DIR} ${PORT_OPTS} ${HOST_OPTS} ${EXTRA_DAEMON_OPTS}"
|
||||
|
||||
SETTINGS_LOADED=TRUE
|
||||
fi
|
||||
|
||||
@@ -99,12 +99,14 @@ DEFAULT_BUFFER_SIZE = -1
|
||||
|
||||
|
||||
class FauxSocket(object):
|
||||
|
||||
"""Faux socket with the minimal interface required by pypy"""
|
||||
|
||||
def _reuse(self):
|
||||
pass
|
||||
|
||||
def _drop(self):
|
||||
pass
|
||||
|
||||
_fileobject_uses_str_type = isinstance(
|
||||
socket._fileobject(FauxSocket())._rbuf, basestring)
|
||||
del FauxSocket # this class is not longer required for anything.
|
||||
|
||||
@@ -42,7 +42,7 @@ class GazelleAPI(object):
|
||||
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'}
|
||||
|
||||
|
||||
def __init__(self, username=None, password=None):
|
||||
def __init__(self, username=None, password=None, url=None):
|
||||
self.session = requests.session()
|
||||
self.session.headers = self.default_headers
|
||||
self.username = username
|
||||
@@ -59,7 +59,7 @@ class GazelleAPI(object):
|
||||
self.cached_torrents = {}
|
||||
self.cached_requests = {}
|
||||
self.cached_categories = {}
|
||||
self.site = "https://what.cd/"
|
||||
self.site = url + "/"
|
||||
self.past_request_timestamps = []
|
||||
|
||||
def wait_for_rate_limit(self):
|
||||
@@ -95,7 +95,7 @@ class GazelleAPI(object):
|
||||
|
||||
self.wait_for_rate_limit()
|
||||
|
||||
loginpage = 'https://what.cd/login.php'
|
||||
loginpage = self.site + 'login.php'
|
||||
data = {'username': self.username,
|
||||
'password': self.password,
|
||||
'keeplogged': '1'}
|
||||
|
||||
Reference in New Issue
Block a user