mirror of
https://github.com/rembo10/headphones.git
synced 2026-07-20 16:03:59 +01:00
Merge branch 'separate-lossless-folders' into develop
This commit is contained in:
@@ -342,10 +342,15 @@ m<%inherit file="base.html"/>
|
|||||||
<input type="checkbox" name="embed_lyrics" value="1" ${config['embed_lyrics']}><label>Embed lyrics</label>
|
<input type="checkbox" name="embed_lyrics" value="1" ${config['embed_lyrics']}><label>Embed lyrics</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label>Path to Destination folder</label>
|
<label>Path to Destination Folder</label>
|
||||||
<input type="text" name="destination_dir" value="${config['dest_dir']}" size="50">
|
<input type="text" name="destination_dir" value="${config['dest_dir']}" size="50">
|
||||||
<small>e.g. /Users/name/Music/iTunes or /Volumes/share/music</small>
|
<small>e.g. /Users/name/Music/iTunes or /Volumes/share/music</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label>Path to Lossless Destination folder (optional)</label>
|
||||||
|
<input type="text" name="lossless_destination_dir" value="${config['lossless_dest_dir']}" size="50">
|
||||||
|
<small>Set this if you have a separate directory for lossless music</small>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -379,8 +384,14 @@ m<%inherit file="base.html"/>
|
|||||||
<input type="checkbox" name="music_encoder" id="music_encoder" value="1" ${config['music_encoder']}/><label>Re-encode downloads during postprocessing</label>
|
<input type="checkbox" name="music_encoder" id="music_encoder" value="1" ${config['music_encoder']}/><label>Re-encode downloads during postprocessing</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="encoderoptions" class="row clearfix checkbox">
|
<div id="encoderoptions" class="row clearfix checkbox">
|
||||||
|
<div class="row">
|
||||||
<input type="checkbox" name="encoderlossless" value="1" ${config['encoderlossless']}/><label>Only re-encode lossless files (.flac)</label>
|
<input type="checkbox" name="encoderlossless" value="1" ${config['encoderlossless']}/><label>Only re-encode lossless files (.flac)</label>
|
||||||
</div>
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<input type="checkbox" name="delete_lossless_files" value="1" ${config['delete_lossless_files']}/><label>Delete original lossless files after encoding</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<%
|
<%
|
||||||
if config['encoder'] == 'lame':
|
if config['encoder'] == 'lame':
|
||||||
lameselect = 'selected="selected"'
|
lameselect = 'selected="selected"'
|
||||||
|
|||||||
+10
-3
@@ -84,6 +84,7 @@ CHECK_GITHUB_INTERVAL = None
|
|||||||
|
|
||||||
MUSIC_DIR = None
|
MUSIC_DIR = None
|
||||||
DESTINATION_DIR = None
|
DESTINATION_DIR = None
|
||||||
|
LOSSLESS_DESTINATION_DIR = None
|
||||||
FOLDER_FORMAT = None
|
FOLDER_FORMAT = None
|
||||||
FILE_FORMAT = None
|
FILE_FORMAT = None
|
||||||
PATH_TO_XML = None
|
PATH_TO_XML = None
|
||||||
@@ -165,6 +166,7 @@ ENCODEROUTPUTFORMAT = None
|
|||||||
ENCODERQUALITY = None
|
ENCODERQUALITY = None
|
||||||
ENCODERVBRCBR = None
|
ENCODERVBRCBR = None
|
||||||
ENCODERLOSSLESS = False
|
ENCODERLOSSLESS = False
|
||||||
|
DELETE_LOSSLESS_FILES = False
|
||||||
PROWL_ENABLED = True
|
PROWL_ENABLED = True
|
||||||
PROWL_PRIORITY = 1
|
PROWL_PRIORITY = 1
|
||||||
PROWL_KEYS = None
|
PROWL_KEYS = None
|
||||||
@@ -238,7 +240,8 @@ def initialize():
|
|||||||
|
|
||||||
global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \
|
global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \
|
||||||
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, \
|
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, \
|
||||||
CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \
|
CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \
|
||||||
|
LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \
|
||||||
ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, \
|
ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, \
|
||||||
ADD_ALBUM_ART, EMBED_ALBUM_ART, EMBED_LYRICS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \
|
ADD_ALBUM_ART, EMBED_ALBUM_ART, EMBED_LYRICS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \
|
||||||
TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, DOWNLOAD_TORRENT_DIR, \
|
TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, DOWNLOAD_TORRENT_DIR, \
|
||||||
@@ -246,7 +249,7 @@ def initialize():
|
|||||||
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS,\
|
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS,\
|
||||||
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \
|
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \
|
||||||
ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, \
|
ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, \
|
||||||
ENCODERLOSSLESS, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, MIRRORLIST, MIRROR, CUSTOMHOST, CUSTOMPORT, \
|
ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, MIRRORLIST, MIRROR, CUSTOMHOST, CUSTOMPORT, \
|
||||||
CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, SYNOINDEX_ENABLED, \
|
CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, SYNOINDEX_ENABLED, \
|
||||||
ALBUM_COMPLETION_PCT
|
ALBUM_COMPLETION_PCT
|
||||||
|
|
||||||
@@ -295,6 +298,7 @@ def initialize():
|
|||||||
|
|
||||||
MUSIC_DIR = check_setting_str(CFG, 'General', 'music_dir', '')
|
MUSIC_DIR = check_setting_str(CFG, 'General', 'music_dir', '')
|
||||||
DESTINATION_DIR = check_setting_str(CFG, 'General', 'destination_dir', '')
|
DESTINATION_DIR = check_setting_str(CFG, 'General', 'destination_dir', '')
|
||||||
|
LOSSLESS_DESTINATION_DIR = check_setting_str(CFG, 'General', 'lossless_destination_dir', '')
|
||||||
PREFERRED_QUALITY = check_setting_int(CFG, 'General', 'preferred_quality', 0)
|
PREFERRED_QUALITY = check_setting_int(CFG, 'General', 'preferred_quality', 0)
|
||||||
PREFERRED_BITRATE = check_setting_int(CFG, 'General', 'preferred_bitrate', '')
|
PREFERRED_BITRATE = check_setting_int(CFG, 'General', 'preferred_bitrate', '')
|
||||||
DETECT_BITRATE = bool(check_setting_int(CFG, 'General', 'detect_bitrate', 0))
|
DETECT_BITRATE = bool(check_setting_int(CFG, 'General', 'detect_bitrate', 0))
|
||||||
@@ -373,6 +377,7 @@ def initialize():
|
|||||||
ENCODERQUALITY = check_setting_int(CFG, 'General', 'encoderquality', 2)
|
ENCODERQUALITY = check_setting_int(CFG, 'General', 'encoderquality', 2)
|
||||||
ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr')
|
ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr')
|
||||||
ENCODERLOSSLESS = bool(check_setting_int(CFG, 'General', 'encoderlossless', 1))
|
ENCODERLOSSLESS = bool(check_setting_int(CFG, 'General', 'encoderlossless', 1))
|
||||||
|
DELETE_LOSSLESS_FILES = bool(check_setting_int(CFG, 'General', 'delete_lossless_files', 1))
|
||||||
|
|
||||||
PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0))
|
PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0))
|
||||||
PROWL_KEYS = check_setting_str(CFG, 'Prowl', 'prowl_keys', '')
|
PROWL_KEYS = check_setting_str(CFG, 'Prowl', 'prowl_keys', '')
|
||||||
@@ -570,6 +575,7 @@ def config_write():
|
|||||||
|
|
||||||
new_config['General']['music_dir'] = MUSIC_DIR
|
new_config['General']['music_dir'] = MUSIC_DIR
|
||||||
new_config['General']['destination_dir'] = DESTINATION_DIR
|
new_config['General']['destination_dir'] = DESTINATION_DIR
|
||||||
|
new_config['General']['lossless_destination_dir'] = LOSSLESS_DESTINATION_DIR
|
||||||
new_config['General']['preferred_quality'] = PREFERRED_QUALITY
|
new_config['General']['preferred_quality'] = PREFERRED_QUALITY
|
||||||
new_config['General']['preferred_bitrate'] = PREFERRED_BITRATE
|
new_config['General']['preferred_bitrate'] = PREFERRED_BITRATE
|
||||||
new_config['General']['detect_bitrate'] = int(DETECT_BITRATE)
|
new_config['General']['detect_bitrate'] = int(DETECT_BITRATE)
|
||||||
@@ -677,7 +683,8 @@ def config_write():
|
|||||||
new_config['General']['encoderoutputformat'] = ENCODEROUTPUTFORMAT
|
new_config['General']['encoderoutputformat'] = ENCODEROUTPUTFORMAT
|
||||||
new_config['General']['encoderquality'] = ENCODERQUALITY
|
new_config['General']['encoderquality'] = ENCODERQUALITY
|
||||||
new_config['General']['encodervbrcbr'] = ENCODERVBRCBR
|
new_config['General']['encodervbrcbr'] = ENCODERVBRCBR
|
||||||
new_config['General']['encoderlossless'] = ENCODERLOSSLESS
|
new_config['General']['encoderlossless'] = int(ENCODERLOSSLESS)
|
||||||
|
new_config['General']['delete_lossless_files'] = int(DELETE_LOSSLESS_FILES)
|
||||||
|
|
||||||
new_config['General']['mirror'] = MIRROR
|
new_config['General']['mirror'] = MIRROR
|
||||||
new_config['General']['customhost'] = CUSTOMHOST
|
new_config['General']['customhost'] = CUSTOMHOST
|
||||||
|
|||||||
+34
-2
@@ -13,10 +13,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import time
|
import os, time
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re, shutil
|
||||||
|
|
||||||
import headphones
|
import headphones
|
||||||
|
|
||||||
@@ -218,3 +218,35 @@ def extract_song_data(s):
|
|||||||
else:
|
else:
|
||||||
logger.info("Couldn't parse " + s + " into a valid Newbin format")
|
logger.info("Couldn't parse " + s + " into a valid Newbin format")
|
||||||
return (name, album, year)
|
return (name, album, year)
|
||||||
|
|
||||||
|
def smartMove(src, dest, delete=True):
|
||||||
|
|
||||||
|
source_dir = os.path.dirname(src)
|
||||||
|
filename = os.path.basename(src)
|
||||||
|
|
||||||
|
if os.path.isfile(os.path.join(dest, filename)):
|
||||||
|
logger.info('Destination file exists: %s' % os.path.join(dest, filename).decode(headphones.SYS_ENCODING))
|
||||||
|
title = os.path.splitext(filename)[0]
|
||||||
|
ext = os.path.splitext(filename)[1]
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
newfile = title + '(' + str(i) + ')' + ext
|
||||||
|
if os.path.isfile(os.path.join(dest, newfile)):
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
logger.info('Renaming to %s' % newfile)
|
||||||
|
try:
|
||||||
|
os.rename(src, os.path.join(source_dir, newfile))
|
||||||
|
filename = newfile
|
||||||
|
except Exception, e:
|
||||||
|
logger.warn('Error renaming %s: %s' % (src.decode(headphones.SYS_ENCODING), e))
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
if delete:
|
||||||
|
shutil.move(os.path.join(source_dir, filename), os.path.join(dest, filename))
|
||||||
|
else:
|
||||||
|
shutil.copy(os.path.join(source_dir, filename), os.path.join(dest, filename))
|
||||||
|
return True
|
||||||
|
except Exception, e:
|
||||||
|
logger.warn('Error moving file %s: %s' % (filename.decode(headphones.SYS_ENCODING), e))
|
||||||
|
|||||||
@@ -138,7 +138,8 @@ def command(encoder,musicSource,musicDest,albumPath):
|
|||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
return_code = call(cmd, shell=True)
|
return_code = call(cmd, shell=True)
|
||||||
if (return_code==0) and (os.path.exists(musicDest)):
|
if (return_code==0) and (os.path.exists(musicDest)):
|
||||||
os.remove(musicSource)
|
if headphones.DELETE_LOSSLESS_FILES:
|
||||||
|
os.remove(musicSource)
|
||||||
shutil.move(musicDest,albumPath)
|
shutil.move(musicDest,albumPath)
|
||||||
logger.info('Music "%s" encoded in %s' % (musicSource,getTimeEncode(startMusicTime)))
|
logger.info('Music "%s" encoded in %s' % (musicSource,getTimeEncode(startMusicTime)))
|
||||||
|
|
||||||
|
|||||||
+183
-88
@@ -263,7 +263,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
|
|||||||
renameFiles(albumpath, downloaded_track_list, release)
|
renameFiles(albumpath, downloaded_track_list, release)
|
||||||
|
|
||||||
if headphones.MOVE_FILES and headphones.DESTINATION_DIR:
|
if headphones.MOVE_FILES and headphones.DESTINATION_DIR:
|
||||||
albumpath = moveFiles(albumpath, release, tracks)
|
albumpaths = moveFiles(albumpath, release, tracks)
|
||||||
|
|
||||||
if headphones.MOVE_FILES and not headphones.DESTINATION_DIR:
|
if headphones.MOVE_FILES and not headphones.DESTINATION_DIR:
|
||||||
logger.error('No DESTINATION_DIR has been set. Set "Destination Directory" to the parent directory you want to move the files to')
|
logger.error('No DESTINATION_DIR has been set. Set "Destination Directory" to the parent directory you want to move the files to')
|
||||||
@@ -273,8 +273,9 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
|
|||||||
myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid])
|
myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid])
|
||||||
myDB.action('UPDATE snatched SET status = "Processed" WHERE AlbumID=?', [albumid])
|
myDB.action('UPDATE snatched SET status = "Processed" WHERE AlbumID=?', [albumid])
|
||||||
|
|
||||||
# Update the have tracks
|
# Update the have tracks for all created dirs:
|
||||||
librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName'])
|
for albumpath in albumpaths:
|
||||||
|
librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName'])
|
||||||
|
|
||||||
logger.info('Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle']))
|
logger.info('Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle']))
|
||||||
|
|
||||||
@@ -297,7 +298,8 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
|
|||||||
|
|
||||||
if headphones.SYNOINDEX_ENABLED:
|
if headphones.SYNOINDEX_ENABLED:
|
||||||
syno = notifiers.Synoindex()
|
syno = notifiers.Synoindex()
|
||||||
syno.notify(albumpath)
|
for albumpath in albumpaths:
|
||||||
|
syno.notify(albumpath)
|
||||||
|
|
||||||
def embedAlbumArt(artwork, downloaded_track_list):
|
def embedAlbumArt(artwork, downloaded_track_list):
|
||||||
logger.info('Embedding album art')
|
logger.info('Embedding album art')
|
||||||
@@ -375,71 +377,138 @@ def moveFiles(albumpath, release, tracks):
|
|||||||
|
|
||||||
if folder.startswith('.'):
|
if folder.startswith('.'):
|
||||||
folder = folder.replace(0, '_')
|
folder = folder.replace(0, '_')
|
||||||
|
|
||||||
|
# Grab our list of files early on so we can determine if we need to create
|
||||||
|
# the lossy_dest_dir, lossless_dest_dir, or both
|
||||||
|
files_to_move = []
|
||||||
|
lossy_media = False
|
||||||
|
lossless_media = False
|
||||||
|
|
||||||
destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, folder)).encode(headphones.SYS_ENCODING)
|
|
||||||
|
|
||||||
last_folder = headphones.FOLDER_FORMAT.split('/')[-1]
|
|
||||||
|
|
||||||
# Only rename the folder if they use the album name, otherwise merge into existing folder
|
|
||||||
if os.path.exists(destination_path) and 'album' in last_folder.lower():
|
|
||||||
i = 1
|
|
||||||
while True:
|
|
||||||
newfolder = folder + '[%i]' % i
|
|
||||||
destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING)
|
|
||||||
if os.path.exists(destination_path):
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
folder = newfolder
|
|
||||||
break
|
|
||||||
|
|
||||||
logger.info('Moving files from %s to %s' % (unicode(albumpath, headphones.SYS_ENCODING, errors="replace"), unicode(destination_path, headphones.SYS_ENCODING, errors="replace")))
|
|
||||||
|
|
||||||
# Basically check if generic/non-album folders already exist, since we're going to merge
|
|
||||||
if not os.path.exists(destination_path):
|
|
||||||
try:
|
|
||||||
os.makedirs(destination_path)
|
|
||||||
except Exception, e:
|
|
||||||
logger.error('Could not create folder for %s. Not moving: %s' % (release['AlbumTitle'], e))
|
|
||||||
return albumpath
|
|
||||||
|
|
||||||
# Move files to the destination folder, renaming them if they already exist
|
|
||||||
for r,d,f in os.walk(albumpath):
|
for r,d,f in os.walk(albumpath):
|
||||||
for files in f:
|
for files in f:
|
||||||
if os.path.isfile(os.path.join(destination_path, files)):
|
files_to_move.append(os.path.join(r, files))
|
||||||
logger.info('Destination file exists: %s' % os.path.join(destination_path, files))
|
if any(files.lower.endswith(x) for x in headphones.LOSSY_MEDIA_FORMATS):
|
||||||
title = os.path.splitext(files)[0]
|
lossy_media = True
|
||||||
ext = os.path.splitext(files)[1]
|
if any(files.lower.endswith(x) for x in headphones.LOSSLESS_MEDIA_FORMATS):
|
||||||
i = 1
|
lossless_media = True
|
||||||
while True:
|
|
||||||
newfile = title + '(' + str(i) + ')' + ext
|
|
||||||
if os.path.isfile(os.path.join(destination_path, newfile)):
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
logger.info('Renaming to %s' % newfile)
|
|
||||||
try:
|
|
||||||
os.rename(os.path.join(r, files), os.path.join(r, newfile))
|
|
||||||
files = newfile
|
|
||||||
except Exception, e:
|
|
||||||
logger.warn('Error renaming %s: %s' % (files, e))
|
|
||||||
break
|
|
||||||
|
|
||||||
|
# Do some sanity checking to see what directories we need to create:
|
||||||
|
make_lossy_folder = False
|
||||||
|
make_lossless_folder = False
|
||||||
|
|
||||||
|
lossy_destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, folder)).encode(headphones.SYS_ENCODING)
|
||||||
|
lossless_destination_path = os.path.normpath(os.path.join(headphones.LOSSLESS_DESTINATION_DIR, folder)).encode(headphones.SYS_ENCODING)
|
||||||
|
|
||||||
|
# If they set a destination dir for lossless media, only create the lossy folder if there is lossy media
|
||||||
|
if headphones.LOSSLESS_DESTINATION_DIR:
|
||||||
|
if lossy_media:
|
||||||
|
make_lossy_folder = True
|
||||||
|
if lossless_media:
|
||||||
|
make_lossless_folder = True
|
||||||
|
# If they haven't set a lossless dest_dir, just create the "lossy" folder
|
||||||
|
else:
|
||||||
|
make_lossy_folder = True
|
||||||
|
|
||||||
|
last_folder = headphones.FOLDER_FORMAT.split('/')[-1]
|
||||||
|
|
||||||
|
if make_lossless_folder:
|
||||||
|
# Only rename the folder if they use the album name, otherwise merge into existing folder
|
||||||
|
if os.path.exists(lossless_destination_path) and 'album' in last_folder.lower():
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
newfolder = folder + '[%i]' % i
|
||||||
|
lossless_destination_path = os.path.normpath(os.path.join(headphones.LOSSLESS_DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING)
|
||||||
|
if os.path.exists(destination_path):
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
folder = newfolder
|
||||||
|
break
|
||||||
|
|
||||||
|
if not os.path.exists(lossless_destination_path):
|
||||||
try:
|
try:
|
||||||
shutil.move(os.path.join(r, files), os.path.join(destination_path, files))
|
os.makedirs(lossless_destination_path)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.warn('Error moving file %s: %s' % (files, e))
|
logger.error('Could not create lossless folder for %s. (Error: %s)' % (release['AlbumTitle'], e))
|
||||||
|
if not make_lossy_folder:
|
||||||
|
return albumpath
|
||||||
|
|
||||||
|
if make_lossy_folder:
|
||||||
|
if os.path.exists(lossy_destination_path) and 'album' in last_folder.lower():
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
newfolder = folder + '[%i]' % i
|
||||||
|
lossy_destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING)
|
||||||
|
if os.path.exists(lossy_destination_path):
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
folder = newfolder
|
||||||
|
break
|
||||||
|
|
||||||
|
if not os.path.exists(lossy_destination_path):
|
||||||
|
try:
|
||||||
|
os.makedirs(lossy_destination_path)
|
||||||
|
except Exception, e:
|
||||||
|
logger.error('Could not create folder for %s. Not moving: %s' % (release['AlbumTitle'], e))
|
||||||
|
return albumpath
|
||||||
|
|
||||||
|
logger.info('Checking which files we need to move.....')
|
||||||
|
|
||||||
|
# Move files to the destination folder, renaming them if they already exist
|
||||||
|
# If we have two desination_dirs, move non-music files to both
|
||||||
|
if make_lossy_folder and make_lossless_folder:
|
||||||
|
|
||||||
|
for file_to_move in files_to_move:
|
||||||
|
|
||||||
|
if any(file_to_move.lower.endswith(x) for x in headphones.LOSSY_MEDIA_FORMATS):
|
||||||
|
helpers.smartMove(file_to_move, lossy_destination_path)
|
||||||
|
|
||||||
|
elif any(files.lower.endswith(x) for x in headphones.LOSSLESS_MEDIA_FORMATS):
|
||||||
|
helpers.smartMove(file_to_move, lossless_destination_path)
|
||||||
|
|
||||||
|
# If it's a non-music file, move it to both dirs
|
||||||
|
else:
|
||||||
|
|
||||||
|
moved_to_lossy_folder = helpers.smartMove(file_to_move, lossy_destination_path, delete=False)
|
||||||
|
moved_to_lossless_folder = helpers.smartMove(file_to_move, lossless_destination_path, delete=False)
|
||||||
|
|
||||||
|
if moved_to_lossy_folder or moved_to_lossless_folder:
|
||||||
|
try:
|
||||||
|
os.remove(file_to_move)
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Error deleting file '" + file_to_move.decode(headphones.SYS_ENCODING) + "' from source directory")
|
||||||
|
else:
|
||||||
|
logger.error("Error copying '" + file_to_move.decode(headphones.SYS_ENCODING) + "'. Not deleting from download directory")
|
||||||
|
|
||||||
|
elif make_lossless_folder and not make_lossy_folder:
|
||||||
|
|
||||||
|
for file_to_move in files_to_move:
|
||||||
|
helpers.smartMove(file_to_move, lossless_destination_path)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
for file_to_move in files_to_move:
|
||||||
|
helpers.smartMove(file_to_move, lossy_destination_path)
|
||||||
|
|
||||||
# Chmod the directories using the folder_format (script courtesy of premiso!)
|
# Chmod the directories using the folder_format (script courtesy of premiso!)
|
||||||
folder_list = folder.split('/')
|
folder_list = folder.split('/')
|
||||||
temp_f = headphones.DESTINATION_DIR
|
temp_fs = []
|
||||||
|
|
||||||
for f in folder_list:
|
if make_lossless_folder:
|
||||||
|
temp_fs.append(headphones.LOSSLESS_DESTINATION_DIR)
|
||||||
temp_f = os.path.join(temp_f, f)
|
|
||||||
|
if make_lossy_folder:
|
||||||
try:
|
temp_fs.append(headphones.DESTINATION_DIR)
|
||||||
os.chmod(os.path.normpath(temp_f).encode(headphones.SYS_ENCODING), int(headphones.FOLDER_PERMISSIONS, 8))
|
|
||||||
except Exception, e:
|
for temp_f in temp_fs:
|
||||||
logger.error("Error trying to change permissions on folder: %s" % temp_f)
|
|
||||||
|
for f in folder_list:
|
||||||
|
|
||||||
|
temp_f = os.path.join(temp_f, f)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.chmod(os.path.normpath(temp_f).encode(headphones.SYS_ENCODING), int(headphones.FOLDER_PERMISSIONS, 8))
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Error trying to change permissions on folder: %s" % temp_f)
|
||||||
|
|
||||||
# If we failed to move all the files out of the directory, this will fail too
|
# If we failed to move all the files out of the directory, this will fail too
|
||||||
try:
|
try:
|
||||||
@@ -447,42 +516,68 @@ def moveFiles(albumpath, release, tracks):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.error('Could not remove directory: %s. %s' % (albumpath, e))
|
logger.error('Could not remove directory: %s. %s' % (albumpath, e))
|
||||||
|
|
||||||
return destination_path
|
destination_paths = []
|
||||||
|
|
||||||
|
if make_lossy_folder:
|
||||||
|
destination_paths.append(lossy_destination_path)
|
||||||
|
if make_lossless_folder:
|
||||||
|
destination_paths.append(lossless_destination_path)
|
||||||
|
|
||||||
|
return destination_paths
|
||||||
|
|
||||||
def correctMetadata(albumid, release, downloaded_track_list):
|
def correctMetadata(albumid, release, downloaded_track_list):
|
||||||
|
|
||||||
logger.info('Writing metadata')
|
logger.info('Preparing to write metadata to tracks....')
|
||||||
items = []
|
lossy_items = []
|
||||||
|
lossless_items = []
|
||||||
|
|
||||||
|
# Process lossless & lossy media formats separately
|
||||||
for downloaded_track in downloaded_track_list:
|
for downloaded_track in downloaded_track_list:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
try:
|
if any(downloaded_track.lower().endswith('.' + x.lower()) for x in headphones.LOSSLESS_MEDIA_FORMATS):
|
||||||
items.append(beets.library.Item.from_path(downloaded_track))
|
lossless_items.append(beets.library.Item.from_path(downloaded_track))
|
||||||
|
elif any(downloaded_track.lower().endswith('.' + x.lower()) for x in headphones.LOSSY_MEDIA_FORMATS):
|
||||||
|
lossy_items.append(beets.library.Item.from_path(downloaded_track))
|
||||||
|
else:
|
||||||
|
logger.warn("Skipping: " + downloaded_track.decode(headphones.SYS_ENCODING) + " because it is not a mutagen friendly file format")
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.error("Beets couldn't create an Item from: " + downloaded_track + " - not a media file?" + str(e))
|
|
||||||
|
logger.error("Beets couldn't create an Item from: " + downloaded_track.decode(headphones.SYS_ENCODING) + " - not a media file?" + str(e))
|
||||||
try:
|
|
||||||
cur_artist, cur_album, candidates, rec = autotag.tag_album(items, search_artist=helpers.latinToAscii(release['ArtistName']), search_album=helpers.latinToAscii(release['AlbumTitle']))
|
for items in [lossy_items, lossless_items]:
|
||||||
except Exception, e:
|
|
||||||
logger.error('Error getting recommendation: %s. Not writing metadata' % e)
|
if not items:
|
||||||
return
|
continue
|
||||||
if rec == 'RECOMMEND_NONE':
|
|
||||||
logger.warn('No accurate album match found for %s, %s - not writing metadata' % (release['ArtistName'], release['AlbumTitle']))
|
|
||||||
return
|
|
||||||
|
|
||||||
dist, info, mapping, extra_items, extra_tracks = candidates[0]
|
|
||||||
logger.debug('Beets recommendation: %s' % rec)
|
|
||||||
autotag.apply_metadata(info, mapping)
|
|
||||||
|
|
||||||
if len(items) != len(downloaded_track_list):
|
|
||||||
logger.warn("Mismatch between number of tracks downloaded and the metadata items, but I'll try to write it anyway")
|
|
||||||
|
|
||||||
i = 1
|
|
||||||
for item in items:
|
|
||||||
try:
|
try:
|
||||||
item.write()
|
cur_artist, cur_album, candidates, rec = autotag.tag_album(items, search_artist=helpers.latinToAscii(release['ArtistName']), search_album=helpers.latinToAscii(release['AlbumTitle']))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.warn('Error writing metadata to track %i: %s' % (i,e))
|
logger.error('Error getting recommendation: %s. Not writing metadata' % e)
|
||||||
i += 1
|
return
|
||||||
|
if rec == 'RECOMMEND_NONE':
|
||||||
|
logger.warn('No accurate album match found for %s, %s - not writing metadata' % (release['ArtistName'], release['AlbumTitle']))
|
||||||
|
return
|
||||||
|
|
||||||
|
if candidates:
|
||||||
|
dist, info, mapping, extra_items, extra_tracks = candidates[0]
|
||||||
|
else:
|
||||||
|
logger.warn('No accurate album match found for %s, %s - not writing metadata' % (release['ArtistName'], release['AlbumTitle']))
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info('Beets recommendation for tagging items: %s' % rec)
|
||||||
|
|
||||||
|
# TODO: Handle extra_items & extra_tracks
|
||||||
|
|
||||||
|
autotag.apply_metadata(info, mapping)
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
try:
|
||||||
|
item.write()
|
||||||
|
logger.info("Successfully applied metadata to: " + item.path.decode(headphones.SYS_ENCODING))
|
||||||
|
except Exception, e:
|
||||||
|
logger.warn("Error writing metadata to " + item.path.decode(headphones.SYS_ENCODING) + ": " + str(e))
|
||||||
|
|
||||||
def embedLyrics(downloaded_track_list):
|
def embedLyrics(downloaded_track_list):
|
||||||
logger.info('Adding lyrics')
|
logger.info('Adding lyrics')
|
||||||
|
|||||||
@@ -429,6 +429,7 @@ class WebInterface(object):
|
|||||||
"embed_album_art" : checked(headphones.EMBED_ALBUM_ART),
|
"embed_album_art" : checked(headphones.EMBED_ALBUM_ART),
|
||||||
"embed_lyrics" : checked(headphones.EMBED_LYRICS),
|
"embed_lyrics" : checked(headphones.EMBED_LYRICS),
|
||||||
"dest_dir" : headphones.DESTINATION_DIR,
|
"dest_dir" : headphones.DESTINATION_DIR,
|
||||||
|
"lossless_dest_dir" : headphones.LOSSLESS_DESTINATION_DIR,
|
||||||
"folder_format" : headphones.FOLDER_FORMAT,
|
"folder_format" : headphones.FOLDER_FORMAT,
|
||||||
"file_format" : headphones.FILE_FORMAT,
|
"file_format" : headphones.FILE_FORMAT,
|
||||||
"include_extras" : checked(headphones.INCLUDE_EXTRAS),
|
"include_extras" : checked(headphones.INCLUDE_EXTRAS),
|
||||||
@@ -446,6 +447,7 @@ class WebInterface(object):
|
|||||||
"encodervbrcbr": headphones.ENCODERVBRCBR,
|
"encodervbrcbr": headphones.ENCODERVBRCBR,
|
||||||
"encoderquality": headphones.ENCODERQUALITY,
|
"encoderquality": headphones.ENCODERQUALITY,
|
||||||
"encoderlossless": checked(headphones.ENCODERLOSSLESS),
|
"encoderlossless": checked(headphones.ENCODERLOSSLESS),
|
||||||
|
"delete_lossless_files": checked(headphones.DELETE_LOSSLESS_FILES),
|
||||||
"prowl_enabled": checked(headphones.PROWL_ENABLED),
|
"prowl_enabled": checked(headphones.PROWL_ENABLED),
|
||||||
"prowl_onsnatch": checked(headphones.PROWL_ONSNATCH),
|
"prowl_onsnatch": checked(headphones.PROWL_ONSNATCH),
|
||||||
"prowl_keys": headphones.PROWL_KEYS,
|
"prowl_keys": headphones.PROWL_KEYS,
|
||||||
@@ -477,8 +479,8 @@ class WebInterface(object):
|
|||||||
usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None, newznab_enabled=0,
|
usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None, newznab_enabled=0,
|
||||||
nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0,
|
nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0,
|
||||||
torrentblackhole_dir=None, download_torrent_dir=None, numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None,
|
torrentblackhole_dir=None, download_torrent_dir=None, numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None,
|
||||||
rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, autowant_upcoming=False, autowant_all=False, interface=None, log_dir=None,
|
rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, lossless_destination_dir=None, folder_format=None, file_format=None, include_extras=0, autowant_upcoming=False, autowant_all=False, interface=None, log_dir=None,
|
||||||
music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0,
|
music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, delete_lossless_files=0,
|
||||||
prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, xbmc_update=0, xbmc_notify=0,
|
prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, xbmc_update=0, xbmc_notify=0,
|
||||||
nma_enabled=False, nma_apikey=None, nma_priority=0, synoindex_enabled=False, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None, **kwargs):
|
nma_enabled=False, nma_apikey=None, nma_priority=0, synoindex_enabled=False, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None, **kwargs):
|
||||||
|
|
||||||
@@ -534,6 +536,7 @@ class WebInterface(object):
|
|||||||
headphones.EMBED_ALBUM_ART = embed_album_art
|
headphones.EMBED_ALBUM_ART = embed_album_art
|
||||||
headphones.EMBED_LYRICS = embed_lyrics
|
headphones.EMBED_LYRICS = embed_lyrics
|
||||||
headphones.DESTINATION_DIR = destination_dir
|
headphones.DESTINATION_DIR = destination_dir
|
||||||
|
headphones.LOSSLESS_DESTINATION_DIR = lossless_destination_dir
|
||||||
headphones.FOLDER_FORMAT = folder_format
|
headphones.FOLDER_FORMAT = folder_format
|
||||||
headphones.FILE_FORMAT = file_format
|
headphones.FILE_FORMAT = file_format
|
||||||
headphones.INCLUDE_EXTRAS = include_extras
|
headphones.INCLUDE_EXTRAS = include_extras
|
||||||
@@ -550,7 +553,8 @@ class WebInterface(object):
|
|||||||
headphones.ENCODEROUTPUTFORMAT = encoderoutputformat
|
headphones.ENCODEROUTPUTFORMAT = encoderoutputformat
|
||||||
headphones.ENCODERVBRCBR = encodervbrcbr
|
headphones.ENCODERVBRCBR = encodervbrcbr
|
||||||
headphones.ENCODERQUALITY = int(encoderquality)
|
headphones.ENCODERQUALITY = int(encoderquality)
|
||||||
headphones.ENCODERLOSSLESS = encoderlossless
|
headphones.ENCODERLOSSLESS = int(encoderlossless)
|
||||||
|
headphones.DELETE_LOSSLESS_FILES = int(delete_lossless_files)
|
||||||
headphones.PROWL_ENABLED = prowl_enabled
|
headphones.PROWL_ENABLED = prowl_enabled
|
||||||
headphones.PROWL_ONSNATCH = prowl_onsnatch
|
headphones.PROWL_ONSNATCH = prowl_onsnatch
|
||||||
headphones.PROWL_KEYS = prowl_keys
|
headphones.PROWL_KEYS = prowl_keys
|
||||||
|
|||||||
Reference in New Issue
Block a user