Skip to content

Commit 4b3b8d2

Browse files
committed
v0.5.11
2 parents 5eeab85 + eeff731 commit 4b3b8d2

25 files changed

+1619
-171
lines changed

.gitignore

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
[Tt]est[Rr]esult*
2+
/cache
3+
/logs
4+
.project
5+
.pydevproject
6+
7+
# coverage generated:
8+
/cover-html/
9+
.coverage
10+
.coveralls.yml
111

212
# Compiled source #
313
###################
@@ -61,8 +71,4 @@ Thumbs.db
6171
obj/
6272
[Rr]elease*/
6373
_ReSharper*/
64-
[Tt]est[Rr]esult*
65-
/cache
66-
/logs
67-
.project
68-
.pydevproject
74+
.vscode

.travis.yml

+23-6
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,36 @@
22
# http://about.travis-ci.org/docs/
33

44
language: python
5+
sudo: false
6+
7+
cache:
8+
pip: true
9+
directories:
10+
- lib
511

612
# Available Python versions:
713
# http://about.travis-ci.org/docs/user/ci-environment/#Python-VM-images
814
python:
915
- "2.6"
10-
- "2.7"
16+
matrix:
17+
include:
18+
- python: "2.7"
19+
env: SENDCOVERAGE=1
20+
1121
# pylint 1.4 does not run under python 2.6
1222
install:
13-
- pip install pyOpenSSL
14-
- pip install pylint==1.3.1
15-
- pip install pyflakes
16-
- pip install pep8
23+
- pip install pyOpenSSL
24+
- pip install pylint==1.3.1
25+
- pip install pyflakes
26+
- pip install pep8
27+
# coverage stuff:
28+
- pip install coveralls
29+
- pip install coverage
1730
script:
1831
- pep8 headphones
1932
- pyflakes headphones
20-
- nosetests headphones
33+
- nosetests
34+
35+
after_success:
36+
# coverage stuff:
37+
- if [ $SENDCOVERAGE ]; then coveralls; fi

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## v0.5.11
4+
Released 20 February 2016
5+
6+
Highlights:
7+
* Added: Soft chroot option
8+
* Fixed: Post processing temporary directory fix (#2504)
9+
* Fixed: Ubuntu init script (#2509)
10+
* Fixed: Image cache uncaught exception (#2485)
11+
* Improved: $Date/$date variable in folder renaming
12+
* Improved: Reuse transmission session id
13+
14+
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.10...v0.5.11).
15+
316
## v0.5.10
417
Released 29 January 2016
518

Headphones.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ def main():
152152
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
153153

154154
# Read config and start logging
155-
headphones.initialize(config_file)
155+
try:
156+
headphones.initialize(config_file)
157+
except headphones.exceptions.SoftChrootError as e:
158+
raise SystemExit('FATAL ERROR')
156159

157160
if headphones.DAEMON:
158161
headphones.daemonize()

data/interfaces/default/config.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,7 @@ <h1 class="clearfix"><i class="fa fa-gear"></i> Settings</h1>
10951095
</div>
10961096
<div class="row">
10971097
<label>Plex Token</label><input type="text" name="plex_token" value="${config['plex_token']}" size="30">
1098-
<small>Plex Token (for use with Plex Home)</small>
1098+
<small>Plex Token (for use with Plex Home)</small>
10991099
</div>
11001100
<div class="checkbox row">
11011101
<input type="checkbox" name="plex_update" value="1" ${config['plex_update']} /><label>Update Plex Library</label>

data/interfaces/default/css/style.css

+11
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,17 @@ textarea,
229229
button {
230230
font: 99%;
231231
}
232+
select {
233+
-moz-border-radius: 5px;
234+
-webkit-border-radius: 5px;
235+
border-radius: 5px;
236+
background: #4F4F4F;
237+
border: 0;
238+
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
239+
color: #fff;
240+
padding: 3px 10px;
241+
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
242+
}
232243
textarea {
233244
overflow: auto;
234245
}

data/interfaces/default/css/style.less

+17-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,23 @@ table {
124124

125125

126126
// Forms
127-
select, input, textarea, button { font: 99%;}
127+
select, input, textarea, button
128+
{
129+
font: 99%;
130+
}
131+
132+
select
133+
{
134+
.rounded(5px);
135+
background: #4F4F4F;
136+
border: 0;
137+
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
138+
color: #fff;
139+
padding: 3px 10px;
140+
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
141+
}
142+
143+
128144
textarea {overflow: auto;}
129145
input { .rounded(3px);}
130146
input:invalid, textarea:invalid {

headphones/__init__.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
from apscheduler.triggers.interval import IntervalTrigger
3030
from headphones import versioncheck, logger
3131
import headphones.config
32-
32+
from headphones.softchroot import SoftChroot
33+
import headphones.exceptions
3334

3435
# (append new extras to the end)
3536
POSSIBLE_EXTRAS = [
@@ -74,6 +75,7 @@
7475
DATA_DIR = None
7576

7677
CONFIG = None
78+
SOFT_CHROOT = None
7779

7880
DB_FILE = None
7981

@@ -92,11 +94,11 @@
9294

9395
UMASK = None
9496

95-
9697
def initialize(config_file):
9798
with INIT_LOCK:
9899

99100
global CONFIG
101+
global SOFT_CHROOT
100102
global _INITIALIZED
101103
global CURRENT_VERSION
102104
global LATEST_VERSION
@@ -136,6 +138,14 @@ def initialize(config_file):
136138
logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR,
137139
verbose=VERBOSE)
138140

141+
try:
142+
SOFT_CHROOT = SoftChroot(str(CONFIG.SOFT_CHROOT))
143+
if SOFT_CHROOT.isEnabled():
144+
logger.info("Soft-chroot enabled for dir: %s", str(CONFIG.SOFT_CHROOT))
145+
except exceptions.SoftChrootError as e:
146+
logger.error("SoftChroot error: %s", e)
147+
raise e
148+
139149
if not CONFIG.CACHE_DIR:
140150
# Put the cache dir in the data dir for now
141151
CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache')

headphones/albumart_test.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#import unittest
2+
#import mock
3+
from headphones.unittestcompat import TestCase
4+
5+
import headphones.albumart
6+
7+
# no tests...
8+
class AlbumArtTest(TestCase):
9+
def test_nothing(self):
10+
x = 100 - 2 * 50
11+
if x:
12+
headphones.albumart.getAlbumArt('asdf')
13+
self.assertTrue(True)

headphones/cache.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def _get_thumb_url(self, data):
116116
return None
117117

118118
for image in images:
119-
if image['size'] == 'medium':
119+
if image['size'] == 'medium' and '#text' in image:
120120
thumb_url = image['#text']
121121
break
122122

headphones/config.py

+33-19
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ def bool_int(value):
1515
value = 0
1616
return int(bool(value))
1717

18+
class path(str):
19+
"""Internal 'marker' type for paths in config."""
20+
21+
@staticmethod
22+
def __call__(val):
23+
return path(val)
24+
25+
def __new__(cls, *args, **kw):
26+
hstr = str.__new__(cls, *args, **kw)
27+
return hstr
28+
29+
def __repr__(self):
30+
return 'headphones.config.path(%s)' % self
1831

1932
_CONFIG_DEFINITIONS = {
2033
'ADD_ALBUM_ART': (int, 'General', 0),
@@ -31,11 +44,11 @@ def bool_int(value):
3144
'AUTO_ADD_ARTISTS': (int, 'General', 1),
3245
'BITRATE': (int, 'General', 192),
3346
'BLACKHOLE': (int, 'General', 0),
34-
'BLACKHOLE_DIR': (str, 'General', ''),
47+
'BLACKHOLE_DIR': (path, 'General', ''),
3548
'BOXCAR_ENABLED': (int, 'Boxcar', 0),
3649
'BOXCAR_ONSNATCH': (int, 'Boxcar', 0),
3750
'BOXCAR_TOKEN': (str, 'Boxcar', ''),
38-
'CACHE_DIR': (str, 'General', ''),
51+
'CACHE_DIR': (path, 'General', ''),
3952
'CACHE_SIZEMB': (int, 'Advanced', 32),
4053
'CHECK_GITHUB': (int, 'General', 1),
4154
'CHECK_GITHUB_INTERVAL': (int, 'General', 360),
@@ -44,21 +57,21 @@ def bool_int(value):
4457
'CONFIG_VERSION': (str, 'General', '0'),
4558
'CORRECT_METADATA': (int, 'General', 0),
4659
'CUE_SPLIT': (int, 'General', 1),
47-
'CUE_SPLIT_FLAC_PATH': (str, 'General', ''),
48-
'CUE_SPLIT_SHNTOOL_PATH': (str, 'General', ''),
60+
'CUE_SPLIT_FLAC_PATH': (path, 'General', ''),
61+
'CUE_SPLIT_SHNTOOL_PATH': (path, 'General', ''),
4962
'CUSTOMAUTH': (int, 'General', 0),
5063
'CUSTOMHOST': (str, 'General', 'localhost'),
5164
'CUSTOMPASS': (str, 'General', ''),
5265
'CUSTOMPORT': (int, 'General', 5000),
5366
'CUSTOMSLEEP': (int, 'General', 1),
5467
'CUSTOMUSER': (str, 'General', ''),
5568
'DELETE_LOSSLESS_FILES': (int, 'General', 1),
56-
'DESTINATION_DIR': (str, 'General', ''),
69+
'DESTINATION_DIR': (path, 'General', ''),
5770
'DETECT_BITRATE': (int, 'General', 0),
5871
'DO_NOT_PROCESS_UNMATCHED': (int, 'General', 0),
59-
'DOWNLOAD_DIR': (str, 'General', ''),
72+
'DOWNLOAD_DIR': (path, 'General', ''),
6073
'DOWNLOAD_SCAN_INTERVAL': (int, 'General', 5),
61-
'DOWNLOAD_TORRENT_DIR': (str, 'General', ''),
74+
'DOWNLOAD_TORRENT_DIR': (path, 'General', ''),
6275
'DO_NOT_OVERRIDE_GIT_BRANCH': (int, 'General', 0),
6376
'EMAIL_ENABLED': (int, 'Email', 0),
6477
'EMAIL_FROM': (str, 'Email', ''),
@@ -74,14 +87,14 @@ def bool_int(value):
7487
'EMBED_LYRICS': (int, 'General', 0),
7588
'ENABLE_HTTPS': (int, 'General', 0),
7689
'ENCODER': (str, 'General', 'ffmpeg'),
77-
'ENCODERFOLDER': (str, 'General', ''),
90+
'ENCODERFOLDER': (path, 'General', ''),
7891
'ENCODERLOSSLESS': (int, 'General', 1),
7992
'ENCODEROUTPUTFORMAT': (str, 'General', 'mp3'),
8093
'ENCODERQUALITY': (int, 'General', 2),
8194
'ENCODERVBRCBR': (str, 'General', 'cbr'),
8295
'ENCODER_MULTICORE': (int, 'General', 0),
8396
'ENCODER_MULTICORE_COUNT': (int, 'General', 0),
84-
'ENCODER_PATH': (str, 'General', ''),
97+
'ENCODER_PATH': (path, 'General', ''),
8598
'EXTRAS': (str, 'General', ''),
8699
'EXTRA_NEWZNABS': (list, 'Newznab', ''),
87100
'EXTRA_TORZNABS': (list, 'Torznab', ''),
@@ -94,7 +107,7 @@ def bool_int(value):
94107
'FOLDER_PERMISSIONS': (str, 'General', '0755'),
95108
'FREEZE_DB': (int, 'General', 0),
96109
'GIT_BRANCH': (str, 'General', 'master'),
97-
'GIT_PATH': (str, 'General', ''),
110+
'GIT_PATH': (path, 'General', ''),
98111
'GIT_USER': (str, 'General', 'rembo10'),
99112
'GROWL_ENABLED': (int, 'Growl', 0),
100113
'GROWL_HOST': (str, 'Growl', ''),
@@ -103,8 +116,8 @@ def bool_int(value):
103116
'HEADPHONES_INDEXER': (bool_int, 'General', False),
104117
'HPPASS': (str, 'General', ''),
105118
'HPUSER': (str, 'General', ''),
106-
'HTTPS_CERT': (str, 'General', ''),
107-
'HTTPS_KEY': (str, 'General', ''),
119+
'HTTPS_CERT': (path, 'General', ''),
120+
'HTTPS_KEY': (path, 'General', ''),
108121
'HTTP_HOST': (str, 'General', 'localhost'),
109122
'HTTP_PASSWORD': (str, 'General', ''),
110123
'HTTP_PORT': (int, 'General', 8181),
@@ -114,8 +127,8 @@ def bool_int(value):
114127
'IDTAG': (int, 'Beets', 0),
115128
'IGNORE_CLEAN_RELEASES': (int, 'General', 0),
116129
'IGNORED_WORDS': (str, 'General', ''),
117-
'IGNORED_FOLDERS': (list, 'Advanced', []),
118-
'IGNORED_FILES': (list, 'Advanced', []),
130+
'IGNORED_FOLDERS': (list, 'Advanced', []), # path
131+
'IGNORED_FILES': (list, 'Advanced', []), # path
119132
'INCLUDE_EXTRAS': (int, 'General', 0),
120133
'INTERFACE': (str, 'General', 'default'),
121134
'JOURNAL_MODE': (str, 'Advanced', 'wal'),
@@ -130,17 +143,17 @@ def bool_int(value):
130143
'LIBRARYSCAN_INTERVAL': (int, 'General', 300),
131144
'LMS_ENABLED': (int, 'LMS', 0),
132145
'LMS_HOST': (str, 'LMS', ''),
133-
'LOG_DIR': (str, 'General', ''),
146+
'LOG_DIR': (path, 'General', ''),
134147
'LOSSLESS_BITRATE_FROM': (int, 'General', 0),
135148
'LOSSLESS_BITRATE_TO': (int, 'General', 0),
136-
'LOSSLESS_DESTINATION_DIR': (str, 'General', ''),
149+
'LOSSLESS_DESTINATION_DIR': (path, 'General', ''),
137150
'MB_IGNORE_AGE': (int, 'General', 365),
138151
'MININOVA': (int, 'Mininova', 0),
139152
'MININOVA_RATIO': (str, 'Mininova', ''),
140153
'MIRROR': (str, 'General', 'musicbrainz.org'),
141154
'MOVE_FILES': (int, 'General', 0),
142155
'MPC_ENABLED': (bool_int, 'MPC', False),
143-
'MUSIC_DIR': (str, 'General', ''),
156+
'MUSIC_DIR': (path, 'General', ''),
144157
'MUSIC_ENCODER': (int, 'General', 0),
145158
'NEWZNAB': (int, 'Newznab', 0),
146159
'NEWZNAB_APIKEY': (str, 'Newznab', ''),
@@ -223,6 +236,7 @@ def bool_int(value):
223236
'SAB_USERNAME': (str, 'SABnzbd', ''),
224237
'SAMPLINGFREQUENCY': (int, 'General', 44100),
225238
'SEARCH_INTERVAL': (int, 'General', 1440),
239+
'SOFT_CHROOT': (path, 'General', ''),
226240
'SONGKICK_APIKEY': (str, 'Songkick', 'nd1We7dFW2RqxPw8'),
227241
'SONGKICK_ENABLED': (int, 'Songkick', 1),
228242
'SONGKICK_FILTER_ENABLED': (int, 'Songkick', 0),
@@ -234,7 +248,7 @@ def bool_int(value):
234248
'SUBSONIC_PASSWORD': (str, 'Subsonic', ''),
235249
'SUBSONIC_USERNAME': (str, 'Subsonic', ''),
236250
'SYNOINDEX_ENABLED': (int, 'Synoindex', 0),
237-
'TORRENTBLACKHOLE_DIR': (str, 'General', ''),
251+
'TORRENTBLACKHOLE_DIR': (path, 'General', ''),
238252
'TORRENT_DOWNLOADER': (int, 'General', 0),
239253
'TORRENT_REMOVAL_INTERVAL': (int, 'General', 720),
240254
'TORZNAB': (int, 'Torznab', 0),
@@ -295,7 +309,7 @@ def _define(self, name):
295309
definition = _CONFIG_DEFINITIONS[key]
296310
if len(definition) == 3:
297311
definition_type, section, default = definition
298-
else:
312+
elif len(definition) == 4:
299313
definition_type, section, _, default = definition
300314
return key, definition_type, section, ini_key, default
301315

0 commit comments

Comments
 (0)