mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-20 02:25:31 +01:00
Merge branch 'develop'
* develop: Release v0.5.3 Active Artist Update can be launched via API Add more logging for issue #2058 Minor code fixes Mark download as frozen if it should not be added Improve logging message. Add support for custom rename tags. Potential fix for #2055
This commit is contained in:
2
API.md
2
API.md
@@ -68,6 +68,8 @@ Unmark album as wanted / i.e. mark as skipped
|
||||
force search for wanted albums - not launched in a separate thread so it may take a bit to complete
|
||||
### forceProcess
|
||||
Force post process albums in download directory - also not launched in a separate thread
|
||||
### forceActiveArtistsUpdate
|
||||
force Active Artist Update - also not launched in a separate thread
|
||||
|
||||
### getVersion
|
||||
Returns some version information: git_path, install_type, current_version, installed_version, commits_behind
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,9 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## v0.5.2
|
||||
Released 28 december 2014
|
||||
## v0.5.3
|
||||
Released 15 January 2015
|
||||
|
||||
Highlight:
|
||||
Highlights:
|
||||
* Added: update active artists via API (#2075)
|
||||
* Fixed: queue instance lacks `add` method (#2055)
|
||||
* Improved: better SSL error messages (#2058)
|
||||
|
||||
The full list of commits can be found [here](https://github.com/rembo10/headphones/compare/v0.5.2...v0.5.3).
|
||||
|
||||
## v0.5.2
|
||||
Released 28 December 2014
|
||||
|
||||
Highlights:
|
||||
* Added: advanced option to ignore certain folders by patterns. (#2037)
|
||||
* Added: advanced option to ignore certain files by patterns (library only)
|
||||
* Added: specify optional paths to CUE splitting tools (#1938)
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=all',$(this),'table')" data-success="All History cleared"><i class="fa fa-trash-o"></i> Clear All History</a>
|
||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Processed',$(this),'table')" data-success="All Processed cleared"><i class="fa fa-trash-o"></i> Clear Processed</a>
|
||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Unprocessed',$(this),'table')" data-success="All Unprocessed cleared"><i class="fa fa-trash-o"></i> Clear Unprocessed</a>
|
||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Frozen',$(this),'table')" data-success="All Frozen cleared"><i class="fa fa-trash-o"></i> Clear Frozen</a>
|
||||
<a id="menu_link_delete" href="#" onclick="doAjaxCall('clearhistory?type=Snatched',$(this),'table')" data-success="All Snatched cleared"><i class="fa fa-trash-o"></i> Clear Snatched</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
@@ -39,9 +40,11 @@
|
||||
grade = 'C'
|
||||
elif item['Status'] == 'Unprocessed':
|
||||
grade = 'X'
|
||||
elif item['Status'] == 'Frozen':
|
||||
grade = 'X'
|
||||
else:
|
||||
grade = 'U'
|
||||
|
||||
|
||||
fileid = 'unknown'
|
||||
if item['URL'].find('nzb') != -1:
|
||||
fileid = 'nzb'
|
||||
|
||||
@@ -13,16 +13,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from headphones import db, mb, importer, searcher, cache, postprocessor, versioncheck, logger
|
||||
from headphones import db, mb, updater, importer, searcher, cache, postprocessor, versioncheck, logger
|
||||
|
||||
import headphones
|
||||
import json
|
||||
|
||||
cmd_list = ['getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs',
|
||||
'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist',
|
||||
'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub',
|
||||
'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo',
|
||||
'getArtistThumb', 'getAlbumThumb', 'choose_specific_download', 'download_specific_release']
|
||||
'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'forceActiveArtistsUpdate',
|
||||
'getVersion', 'checkGithub','shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt',
|
||||
'getArtistInfo', 'getAlbumInfo', 'getArtistThumb', 'getAlbumThumb',
|
||||
'choose_specific_download', 'download_specific_release']
|
||||
|
||||
|
||||
class Api(object):
|
||||
@@ -321,6 +322,9 @@ class Api(object):
|
||||
self.dir = kwargs['dir']
|
||||
postprocessor.forcePostProcess(self.dir)
|
||||
|
||||
def _forceActiveArtistsUpdate(self, **kwargs):
|
||||
updater.dbUpdate()
|
||||
|
||||
def _getVersion(self, **kwargs):
|
||||
self.data = {
|
||||
'git_path': headphones.CONFIG.GIT_PATH,
|
||||
|
||||
@@ -64,20 +64,18 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None,
|
||||
encoded_track_string = track['Location'].encode(headphones.SYS_ENCODING, 'replace')
|
||||
if not os.path.isfile(encoded_track_string):
|
||||
if track['ArtistName']:
|
||||
#Make sure deleted files get accounted for when updating artist track counts
|
||||
# Make sure deleted files get accounted for when updating artist track counts
|
||||
new_artists.append(track['ArtistName'])
|
||||
myDB.action('DELETE FROM have WHERE Location=?', [track['Location']])
|
||||
logger.info('File %s removed from Headphones, as it is no longer on disk' % encoded_track_string.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
###############myDB.action('DELETE from have')
|
||||
|
||||
bitrates = []
|
||||
|
||||
song_list = []
|
||||
latest_subdirectory = []
|
||||
|
||||
new_song_count = 0
|
||||
file_count = 0
|
||||
|
||||
latest_subdirectory = []
|
||||
|
||||
for r, d, f in helpers.walk_directory(dir):
|
||||
# Filter paths based on config. Note that these methods work directly
|
||||
# on the inputs
|
||||
@@ -313,7 +311,6 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None,
|
||||
]
|
||||
|
||||
# Update track counts
|
||||
|
||||
for artist in artists_checked:
|
||||
# Have tracks are selected from tracks table and not all tracks because of duplicates
|
||||
# We update the track count upon an album switch to compliment this
|
||||
@@ -321,13 +318,13 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None,
|
||||
len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistName like ? AND Location IS NOT NULL', [artist]))
|
||||
+ len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND Matched = "Failed"', [artist]))
|
||||
)
|
||||
#Note, some people complain about having "artist have tracks" > # of tracks total in artist official releases
|
||||
# Note: some people complain about having "artist have tracks" > # of tracks total in artist official releases
|
||||
# (can fix by getting rid of second len statement)
|
||||
myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistName=?', [havetracks, artist])
|
||||
|
||||
logger.info('Found %i new artists' % len(artist_list))
|
||||
|
||||
if len(artist_list):
|
||||
if artist_list:
|
||||
if headphones.CONFIG.AUTO_ADD_ARTISTS:
|
||||
logger.info('Importing %i new artists' % len(artist_list))
|
||||
importer.artistlist_to_mbids(artist_list)
|
||||
|
||||
@@ -19,6 +19,7 @@ import shutil
|
||||
import uuid
|
||||
import beets
|
||||
import threading
|
||||
import itertools
|
||||
import headphones
|
||||
|
||||
from beets import autotag
|
||||
@@ -33,7 +34,7 @@ postprocessor_lock = threading.Lock()
|
||||
|
||||
|
||||
def checkFolder():
|
||||
logger.info("Checking download folder for completed downloads")
|
||||
logger.info("Checking download folder for completed downloads (only snatched ones).")
|
||||
|
||||
with postprocessor_lock:
|
||||
myDB = db.DBConnection()
|
||||
@@ -55,7 +56,7 @@ def checkFolder():
|
||||
else:
|
||||
logger.info("No folder name found for " + album['Title'])
|
||||
|
||||
logger.info("Checking download folder finished")
|
||||
logger.info("Checking download folder finished.")
|
||||
|
||||
def verify(albumid, albumpath, Kind=None, forced=False):
|
||||
|
||||
@@ -98,6 +99,11 @@ def verify(albumid, albumpath, Kind=None, forced=False):
|
||||
"but database is frozen. Will skip postprocessing for " \
|
||||
"album with rgid: %s", release_dict['artist_name'],
|
||||
release_dict['artist_id'], albumid)
|
||||
|
||||
myDB.action('UPDATE snatched SET status = "Frozen" WHERE status NOT LIKE "Seed%" and AlbumID=?', [albumid])
|
||||
frozen = re.search(r' \(Frozen\)(?:\[\d+\])?', albumpath)
|
||||
if not frozen:
|
||||
renameUnprocessedFolder(albumpath, tag="Frozen")
|
||||
return
|
||||
|
||||
logger.info(u"Now adding/updating artist: " + release_dict['artist_name'])
|
||||
@@ -188,7 +194,7 @@ def verify(albumid, albumpath, Kind=None, forced=False):
|
||||
myDB.action('UPDATE snatched SET status = "Unprocessed" WHERE status NOT LIKE "Seed%" and AlbumID=?', [albumid])
|
||||
processed = re.search(r' \(Unprocessed\)(?:\[\d+\])?', albumpath)
|
||||
if not processed:
|
||||
renameUnprocessedFolder(albumpath)
|
||||
renameUnprocessedFolder(albumpath, tag="Unprocessed")
|
||||
return
|
||||
|
||||
# test #1: metadata - usually works
|
||||
@@ -272,9 +278,7 @@ def verify(albumid, albumpath, Kind=None, forced=False):
|
||||
myDB.action('UPDATE snatched SET status = "Unprocessed" WHERE status NOT LIKE "Seed%" and AlbumID=?', [albumid])
|
||||
processed = re.search(r' \(Unprocessed\)(?:\[\d+\])?', albumpath)
|
||||
if not processed:
|
||||
renameUnprocessedFolder(albumpath)
|
||||
else:
|
||||
logger.info(u"Already marked as unprocessed: " + albumpath.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
renameUnprocessedFolder(albumpath, tag="Unprocessed")
|
||||
|
||||
|
||||
def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, Kind=None):
|
||||
@@ -1024,20 +1028,22 @@ def updateFilePermissions(albumpaths):
|
||||
continue
|
||||
|
||||
|
||||
def renameUnprocessedFolder(albumpath):
|
||||
def renameUnprocessedFolder(path, tag):
|
||||
"""
|
||||
Rename a unprocessed folder to a new unique name to indicate a certain
|
||||
status.
|
||||
"""
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
for i in itertools.count():
|
||||
if i == 0:
|
||||
new_folder_name = albumpath + ' (Unprocessed)'
|
||||
new_path = "%s (%s)" % (path, tag)
|
||||
else:
|
||||
new_folder_name = albumpath + ' (Unprocessed)[%i]' % i
|
||||
new_path = "%s (%s[%d])" % (path, tag, i)
|
||||
|
||||
if os.path.exists(new_folder_name):
|
||||
if os.path.exists(new_path):
|
||||
i += 1
|
||||
|
||||
else:
|
||||
os.rename(albumpath, new_folder_name)
|
||||
os.rename(path, new_path)
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -76,10 +76,15 @@ def request_response(url, method="get", auto_raise=True,
|
||||
response.raise_for_status()
|
||||
|
||||
return response
|
||||
except requests.exceptions.SSLError:
|
||||
logger.error("Unable to connect to remote host because of a SSL " \
|
||||
"error. It's likely the remote certificate is untrusted by your " \
|
||||
"system. This check can be disabled (advanced users only).")
|
||||
except requests.exceptions.SSLError as e:
|
||||
if not kwargs["verify"]:
|
||||
logger.error("Unable to connect to remote host because of a SSL " \
|
||||
"error. It's likely the remote certificate is untrusted by " \
|
||||
"your system. This check can be disabled (advanced users " \
|
||||
"only).")
|
||||
else:
|
||||
logger.error("SSL error raised during connection, even with " \
|
||||
"SSL certificate verification turned off: %s", e)
|
||||
except requests.ConnectionError:
|
||||
logger.error(
|
||||
"Unable to connect to remote host. Check if the remote "
|
||||
|
||||
Reference in New Issue
Block a user