mirror of
https://github.com/rembo10/headphones.git
synced 2026-03-21 20:29:27 +00:00
Merge remote-tracking branch 'upstream/develop' into feature/refactor_config
Conflicts: headphones/postprocessor.py
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import headphones
|
||||
from headphones import db, logger
|
||||
from headphones import db, logger, cache
|
||||
|
||||
def switch(AlbumID, ReleaseID):
|
||||
'''
|
||||
@@ -42,6 +42,11 @@ def switch(AlbumID, ReleaseID):
|
||||
|
||||
myDB.upsert("albums", newValueDict, controlValueDict)
|
||||
|
||||
# Update cache
|
||||
c = cache.Cache()
|
||||
c.remove_from_cache(AlbumID=AlbumID)
|
||||
c.get_artwork_from_cache(AlbumID=AlbumID)
|
||||
|
||||
for track in newtrackdata:
|
||||
|
||||
controlValueDict = {"TrackID": track['TrackID'],
|
||||
|
||||
@@ -242,6 +242,36 @@ class Cache(object):
|
||||
|
||||
return {'artwork' : image_url, 'thumbnail' : thumb_url }
|
||||
|
||||
def remove_from_cache(self, ArtistID=None, AlbumID=None):
|
||||
"""
|
||||
Pass a musicbrainz id to this function (either ArtistID or AlbumID)
|
||||
"""
|
||||
|
||||
if ArtistID:
|
||||
self.id = ArtistID
|
||||
self.id_type = 'artist'
|
||||
else:
|
||||
self.id = AlbumID
|
||||
self.id_type = 'album'
|
||||
|
||||
self.query_type = 'artwork'
|
||||
|
||||
if self._exists('artwork'):
|
||||
for artwork_file in self.artwork_files:
|
||||
try:
|
||||
os.remove(artwork_file)
|
||||
except:
|
||||
logger.warn('Error deleting file from the cache: %s', artwork_file)
|
||||
|
||||
self.query_type = 'thumb'
|
||||
|
||||
if self._exists('thumb'):
|
||||
for thumb_file in self.thumb_files:
|
||||
try:
|
||||
os.remove(thumb_file)
|
||||
except Exception as e:
|
||||
logger.warn('Error deleting file from the cache: %s', thumb_file)
|
||||
|
||||
def _update_cache(self):
|
||||
'''
|
||||
Since we call the same url for both info and artwork, we'll update both at the same time
|
||||
@@ -249,6 +279,7 @@ class Cache(object):
|
||||
myDB = db.DBConnection()
|
||||
|
||||
# Since lastfm uses release ids rather than release group ids for albums, we have to do a artist + album search for albums
|
||||
# Exception is when adding albums manually, then we should use release id
|
||||
if self.id_type == 'artist':
|
||||
|
||||
data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=LASTFM_API_KEY)
|
||||
@@ -278,8 +309,13 @@ class Cache(object):
|
||||
|
||||
else:
|
||||
|
||||
dbartist = myDB.action('SELECT ArtistName, AlbumTitle FROM albums WHERE AlbumID=?', [self.id]).fetchone()
|
||||
data = lastfm.request_lastfm("album.getinfo", artist=dbartist['ArtistName'], album=dbartist['AlbumTitle'], api_key=LASTFM_API_KEY)
|
||||
dbalbum = myDB.action('SELECT ArtistName, AlbumTitle, ReleaseID FROM albums WHERE AlbumID=?', [self.id]).fetchone()
|
||||
if dbalbum['ReleaseID'] != self.id:
|
||||
data = lastfm.request_lastfm("album.getinfo", mbid=dbalbum['ReleaseID'], api_key=LASTFM_API_KEY)
|
||||
if not data:
|
||||
data = lastfm.request_lastfm("album.getinfo", artist=dbalbum['ArtistName'], album=dbalbum['AlbumTitle'], api_key=LASTFM_API_KEY)
|
||||
else:
|
||||
data = lastfm.request_lastfm("album.getinfo", artist=dbalbum['ArtistName'], album=dbalbum['AlbumTitle'], api_key=LASTFM_API_KEY)
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
664
headphones/cuesplit.py
Executable file
664
headphones/cuesplit.py
Executable file
@@ -0,0 +1,664 @@
|
||||
# This file is part of Headphones.
|
||||
#
|
||||
# Headphones is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Headphones is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Most of this lifted from here: https://github.com/SzieberthAdam/gneposis-cdgrab
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import shutil
|
||||
import commands
|
||||
import subprocess
|
||||
import time
|
||||
import copy
|
||||
import glob
|
||||
|
||||
import headphones
|
||||
from headphones import logger
|
||||
from mutagen.flac import FLAC
|
||||
|
||||
CUE_HEADER = {
|
||||
'genre': '^REM GENRE (.+?)$',
|
||||
'date': '^REM DATE (.+?)$',
|
||||
'discid': '^REM DISCID (.+?)$',
|
||||
'comment': '^REM COMMENT (.+?)$',
|
||||
'catalog': '^CATALOG (.+?)$',
|
||||
'artist': '^PERFORMER (.+?)$',
|
||||
'title': '^TITLE (.+?)$',
|
||||
'file': '^FILE (.+?) WAVE$',
|
||||
'accurateripid': '^REM ACCURATERIPID (.+?)$'
|
||||
}
|
||||
|
||||
CUE_TRACK = 'TRACK (\d\d) AUDIO$'
|
||||
|
||||
CUE_TRACK_INFO = {
|
||||
'artist': 'PERFORMER (.+?)$',
|
||||
'title': 'TITLE (.+?)$',
|
||||
'isrc': 'ISRC (.+?)$',
|
||||
'index': 'INDEX (\d\d) (.+?)$'
|
||||
}
|
||||
|
||||
ALBUM_META_FILE_NAME = 'album.dat'
|
||||
SPLIT_FILE_NAME = 'split.dat'
|
||||
|
||||
ALBUM_META_ALBUM_BY_CUE = ('artist', 'title', 'date', 'genre')
|
||||
|
||||
HTOA_LENGTH_TRIGGER = 3
|
||||
|
||||
WAVE_FILE_TYPE_BY_EXTENSION = {
|
||||
'.wav': 'Waveform Audio',
|
||||
'.wv': 'WavPack',
|
||||
'.ape': "Monkey's Audio",
|
||||
'.m4a': 'Apple Lossless',
|
||||
'.flac': 'Free Lossless Audio Codec'
|
||||
}
|
||||
|
||||
# TODO: Only alow flac for now
|
||||
#SHNTOOL_COMPATIBLE = ('Waveform Audio', 'WavPack', 'Free Lossless Audio Codec')
|
||||
SHNTOOL_COMPATIBLE = ('Free Lossless Audio Codec')
|
||||
|
||||
def check_splitter(command):
|
||||
'''Check xld or shntools installed'''
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
if 'xld' in command:
|
||||
env['PATH'] += os.pathsep + '/Applications'
|
||||
devnull = open(os.devnull)
|
||||
subprocess.Popen([command], stdout=devnull, stderr=devnull, env=env).communicate()
|
||||
except OSError as e:
|
||||
if e.errno == os.errno.ENOENT:
|
||||
return False
|
||||
return True
|
||||
|
||||
def split_baby(split_file, split_cmd):
|
||||
'''Let's split baby'''
|
||||
logger.info('Splitting %s...', split_file.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
logger.debug(subprocess.list2cmdline(split_cmd))
|
||||
|
||||
# Prevent Windows from opening a terminal window
|
||||
startupinfo = None
|
||||
|
||||
if headphones.SYS_PLATFORM == "win32":
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
try:
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
except AttributeError:
|
||||
startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW
|
||||
|
||||
env = os.environ.copy()
|
||||
if 'xld' in split_cmd:
|
||||
env['PATH'] += os.pathsep + '/Applications'
|
||||
|
||||
process = subprocess.Popen(split_cmd, startupinfo=startupinfo,
|
||||
|
||||
stdin=open(os.devnull, 'rb'), stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, env=env)
|
||||
stdout, stderr = process.communicate()
|
||||
if process.returncode:
|
||||
logger.error('Split failed for %s', split_file.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
out = stdout if stdout else stderr
|
||||
logger.error('Error details: %s', out.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
return False
|
||||
else:
|
||||
logger.info('Split success %s', split_file.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
return True
|
||||
|
||||
def check_list(list, ignore=0):
|
||||
'''Checks a list for None elements. If list have None (after ignore index) then it should pass only if all elements
|
||||
are None threreafter. Returns a tuple without the None entries.'''
|
||||
|
||||
if ignore:
|
||||
try:
|
||||
list[int(ignore)]
|
||||
except:
|
||||
raise ValueError('non-integer ignore index or ignore index not in list')
|
||||
|
||||
list1 = list[:ignore]
|
||||
list2 = list[ignore:]
|
||||
|
||||
try:
|
||||
first_none = list2.index(None)
|
||||
except:
|
||||
return tuple(list1 + list2)
|
||||
|
||||
for i in range(first_none, len(list2)):
|
||||
if list2[i]:
|
||||
raise ValueError('non-None entry after None entry in list at index {0}'.format(i))
|
||||
|
||||
while True:
|
||||
list2.remove(None)
|
||||
try:
|
||||
list2.index(None)
|
||||
except:
|
||||
break
|
||||
|
||||
return tuple(list1+list2)
|
||||
|
||||
def trim_cue_entry(string):
|
||||
'''Removes leading and trailing "s.'''
|
||||
if string[0] == '"' and string[-1] == '"':
|
||||
string = string[1:-1]
|
||||
return string
|
||||
|
||||
def int_to_str(value, length=2):
|
||||
'''Converts integer to string eg 3 to "03"'''
|
||||
try:
|
||||
int(value)
|
||||
except:
|
||||
raise ValueError('expected an integer value')
|
||||
|
||||
content = str(value)
|
||||
while len(content) < length:
|
||||
content = '0' + content
|
||||
return content
|
||||
|
||||
def split_file_list(ext=None):
|
||||
file_list = [None for m in range(100)]
|
||||
if ext and ext[0] != '.':
|
||||
ext = '.' + ext
|
||||
for f in os.listdir('.'):
|
||||
if f[:11] == 'split-track':
|
||||
if (ext and ext == os.path.splitext(f)[-1]) or not ext:
|
||||
filename_parser = re.search('split-track(\d\d)', f)
|
||||
track_nr = int(filename_parser.group(1))
|
||||
if cue.htoa() and not os.path.exists('split-track00'+ext):
|
||||
track_nr -= 1
|
||||
file_list[track_nr] = WaveFile(f, track_nr=track_nr)
|
||||
return check_list(file_list, ignore=1)
|
||||
|
||||
|
||||
class Directory:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.name = os.path.split(self.path)[-1]
|
||||
self.content = []
|
||||
self.update()
|
||||
|
||||
def filter(self, classname):
|
||||
content = []
|
||||
for c in self.content:
|
||||
if c.__class__.__name__ == classname:
|
||||
content.append(c)
|
||||
return content
|
||||
|
||||
def tracks(self, ext=None, split=False):
|
||||
content = []
|
||||
for c in self.content:
|
||||
ext_match = False
|
||||
if c.__class__.__name__ == 'WaveFile':
|
||||
if not ext or (ext and ext == c.name_ext):
|
||||
ext_match = True
|
||||
if ext_match and c.track_nr:
|
||||
if not split or (split and c.split_file):
|
||||
content.append(c)
|
||||
return content
|
||||
|
||||
def update(self):
|
||||
def check_match(filename):
|
||||
for i in self.content:
|
||||
if i.name == filename:
|
||||
return True
|
||||
return False
|
||||
|
||||
def identify_track_number(filename):
|
||||
if 'split-track' in filename:
|
||||
search = re.search('split-track(\d\d)', filename)
|
||||
if search:
|
||||
n = int(search.group(1))
|
||||
if n:
|
||||
return n
|
||||
for n in range(0,100):
|
||||
search = re.search(int_to_str(n), filename)
|
||||
if search:
|
||||
# TODO: not part of other value such as year
|
||||
return n
|
||||
|
||||
list_dir = glob.glob(os.path.join(self.path, '*'))
|
||||
|
||||
# TODO: for some reason removes only one file
|
||||
rem_list = []
|
||||
for i in self.content:
|
||||
if i.name not in list_dir:
|
||||
rem_list.append(i)
|
||||
for i in rem_list:
|
||||
self.content.remove(i)
|
||||
|
||||
for i in list_dir:
|
||||
if not check_match(i):
|
||||
# music file
|
||||
if os.path.splitext(i)[-1] in WAVE_FILE_TYPE_BY_EXTENSION.keys():
|
||||
track_nr = identify_track_number(i)
|
||||
if track_nr:
|
||||
self.content.append(WaveFile(self.path + os.sep + i, track_nr=track_nr))
|
||||
else:
|
||||
self.content.append(WaveFile(self.path + os.sep + i))
|
||||
|
||||
# cue file
|
||||
elif os.path.splitext(i)[-1] == '.cue':
|
||||
self.content.append(CueFile(self.path + os.sep + i))
|
||||
|
||||
# meta file
|
||||
elif i == ALBUM_META_FILE_NAME:
|
||||
self.content.append(MetaFile(self.path + os.sep + i))
|
||||
|
||||
# directory
|
||||
elif os.path.isdir(i):
|
||||
self.content.append(Directory(self.path + os.sep + i))
|
||||
|
||||
else:
|
||||
self.content.append(File(self.path + os.sep + i))
|
||||
|
||||
class File:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.name = os.path.split(self.path)[-1]
|
||||
|
||||
self.name_name = ''.join(os.path.splitext(self.name)[:-1])
|
||||
self.name_ext = os.path.splitext(self.name)[-1]
|
||||
self.split_file = True if self.name_name[:11] == 'split-track' else False
|
||||
|
||||
def get_name(self, ext=True, cmd=False):
|
||||
|
||||
if ext == True:
|
||||
content = self.name
|
||||
elif ext == False:
|
||||
content = self.name_name
|
||||
elif ext[0] == '.':
|
||||
content = self.name_name + ext
|
||||
else:
|
||||
raise ValueError('ext parameter error')
|
||||
|
||||
if cmd:
|
||||
content = content.replace(' ', '\ ')
|
||||
|
||||
return content
|
||||
|
||||
class CueFile(File):
|
||||
def __init__(self, path):
|
||||
|
||||
def header_parser():
|
||||
global line_content
|
||||
c = self.content.splitlines()
|
||||
header_dict = {}
|
||||
#remaining_headers = CUE_HEADER
|
||||
remaining_headers = copy.copy(CUE_HEADER)
|
||||
line_index = 0
|
||||
match = True
|
||||
while match:
|
||||
match = False
|
||||
saved_match = None
|
||||
line_content = c[line_index]
|
||||
for e in remaining_headers:
|
||||
search_result = re.search(remaining_headers[e], line_content, re.I)
|
||||
if search_result:
|
||||
search_content = trim_cue_entry(search_result.group(1))
|
||||
header_dict[e] = search_content
|
||||
saved_match = e
|
||||
match = True
|
||||
line_index += 1
|
||||
if saved_match:
|
||||
del remaining_headers[saved_match]
|
||||
return header_dict, line_index
|
||||
|
||||
def track_parser(start_line):
|
||||
c = self.content.splitlines()
|
||||
line_index = start_line
|
||||
line_content = c[line_index]
|
||||
search_result = re.search(CUE_TRACK, line_content, re.I)
|
||||
if not search_result:
|
||||
raise ValueError('inconsistent CUE sheet, TRACK expected at line {0}'.format(line_index+1))
|
||||
track_nr = int(search_result.group(1))
|
||||
line_index += 1
|
||||
next_track = False
|
||||
track_meta = {}
|
||||
# we make room for future indexes
|
||||
track_meta['index'] = [None for m in range(100)]
|
||||
|
||||
while not next_track:
|
||||
if line_index < len(c):
|
||||
line_content = c[line_index]
|
||||
|
||||
artist_search = re.search(CUE_TRACK_INFO['artist'], line_content, re.I)
|
||||
title_search = re.search(CUE_TRACK_INFO['title'], line_content, re.I)
|
||||
isrc_search = re.search(CUE_TRACK_INFO['isrc'], line_content, re.I)
|
||||
index_search = re.search(CUE_TRACK_INFO['index'], line_content, re.I)
|
||||
|
||||
if artist_search:
|
||||
if trim_cue_entry(artist_search.group(1)) != self.header['artist']:
|
||||
track_meta['artist'] = trim_cue_entry(artist_search.group(1))
|
||||
line_index += 1
|
||||
elif title_search:
|
||||
track_meta['title'] = trim_cue_entry(title_search.group(1))
|
||||
line_index += 1
|
||||
elif isrc_search:
|
||||
track_meta['isrc'] = trim_cue_entry(isrc_search.group(1))
|
||||
line_index += 1
|
||||
elif index_search:
|
||||
track_meta['index'][int(index_search.group(1))] = index_search.group(2)
|
||||
line_index += 1
|
||||
elif re.search(CUE_TRACK, line_content, re.I):
|
||||
next_track = True
|
||||
elif line_index == len(c)-1 and not line_content:
|
||||
# last line is empty
|
||||
line_index += 1
|
||||
elif re.search('FLAGS DCP$', line_content, re.I):
|
||||
track_meta['dcpflag'] = True
|
||||
line_index += 1
|
||||
else:
|
||||
raise ValueError('unknown entry in track error, line {0}'.format(line_index+1))
|
||||
else:
|
||||
next_track = True
|
||||
|
||||
track_meta['index'] = check_list(track_meta['index'], ignore=1)
|
||||
|
||||
return track_nr, track_meta, line_index
|
||||
|
||||
File.__init__(self, path)
|
||||
|
||||
try:
|
||||
with open(self.name) as cue_file:
|
||||
self.content = cue_file.read()
|
||||
except:
|
||||
self.content = None
|
||||
|
||||
if not self.content:
|
||||
try:
|
||||
with open(self.name, encoding="cp1252") as cue_file:
|
||||
self.content = cue_file.read()
|
||||
except:
|
||||
raise ValueError('Cant encode CUE Sheet.')
|
||||
|
||||
if self.content[0] == '\ufeff':
|
||||
self.content = self.content[1:]
|
||||
|
||||
header = header_parser()
|
||||
|
||||
self.header = header[0]
|
||||
|
||||
line_index = header[1]
|
||||
|
||||
# we make room for tracks
|
||||
tracks = [None for m in range(100)]
|
||||
|
||||
while line_index < len(self.content.splitlines()):
|
||||
parsed_track = track_parser(line_index)
|
||||
line_index = parsed_track[2]
|
||||
tracks[parsed_track[0]] = parsed_track[1]
|
||||
|
||||
self.tracks = check_list(tracks, ignore=1)
|
||||
|
||||
def get_meta(self):
|
||||
content = ''
|
||||
for i in ALBUM_META_ALBUM_BY_CUE:
|
||||
if self.header.get(i):
|
||||
content += i + '\t' + self.header[i] + '\n'
|
||||
else:
|
||||
content += i + '\t' + '\n'
|
||||
|
||||
for i in range(len(self.tracks)):
|
||||
if self.tracks[i]:
|
||||
if self.tracks[i].get('artist'):
|
||||
content += 'track'+int_to_str(i) + 'artist' + '\t' + self.tracks[i].get('artist') + '\n'
|
||||
if self.tracks[i].get('title'):
|
||||
content += 'track'+int_to_str(i) + 'title' + '\t' + self.tracks[i].get('title') + '\n'
|
||||
return content
|
||||
|
||||
def htoa(self):
|
||||
'''Returns true if Hidden Track exists.'''
|
||||
if int(self.tracks[1]['index'][1][-5:-3]) >= HTOA_LENGTH_TRIGGER:
|
||||
return True
|
||||
return False
|
||||
|
||||
def breakpoints(self):
|
||||
'''Returns track break points. Identical as CUETools' cuebreakpoints, with the exception of my standards for HTOA.'''
|
||||
content = ''
|
||||
for t in range(len(self.tracks)):
|
||||
if t == 1 and not self.htoa():
|
||||
content += ''
|
||||
elif t >= 1:
|
||||
t_index = self.tracks[t]['index']
|
||||
content += t_index[1]
|
||||
if (t < len(self.tracks) - 1):
|
||||
content += '\n'
|
||||
return content
|
||||
|
||||
class MetaFile(File):
|
||||
def __init__(self, path):
|
||||
File.__init__(self, path)
|
||||
with open(self.path) as meta_file:
|
||||
self.rawcontent = meta_file.read()
|
||||
|
||||
content = {}
|
||||
content['tracks'] = [None for m in range(100)]
|
||||
|
||||
for l in self.rawcontent.splitlines():
|
||||
parsed_line = re.search('^(.+?)\t(.+?)$', l)
|
||||
if parsed_line:
|
||||
if parsed_line.group(1)[:5] == 'track':
|
||||
parsed_track = re.search('^track(\d\d)(.+?)$', parsed_line.group(1))
|
||||
if not parsed_track:
|
||||
raise ValueError('Syntax error in album meta file')
|
||||
if not content['tracks'][int(parsed_track.group(1))]:
|
||||
content['tracks'][int(parsed_track.group(1))] = dict()
|
||||
content['tracks'][int(parsed_track.group(1))][parsed_track.group(2)] = parsed_line.group(2)
|
||||
else:
|
||||
content[parsed_line.group(1)] = parsed_line.group(2)
|
||||
|
||||
content['tracks'] = check_list(content['tracks'], ignore=1)
|
||||
|
||||
self.content = content
|
||||
|
||||
def flac_tags(self, track_nr):
|
||||
common_tags = dict()
|
||||
freeform_tags = dict()
|
||||
|
||||
# common flac tags
|
||||
common_tags['artist'] = self.content['artist']
|
||||
common_tags['album'] = self.content['title']
|
||||
common_tags['title'] = self.content['tracks'][track_nr]['title']
|
||||
common_tags['date'] = self.content['date']
|
||||
common_tags['tracknumber'] = str(track_nr)
|
||||
common_tags['genre'] = meta.content['genre']
|
||||
common_tags['tracktotal'] = str(len(self.content['tracks'])-1)
|
||||
|
||||
#freeform tags
|
||||
#freeform_tags['country'] = self.content['country']
|
||||
#freeform_tags['releasedate'] = self.content['releasedate']
|
||||
|
||||
return common_tags, freeform_tags
|
||||
|
||||
def folders(self):
|
||||
artist = self.content['artist']
|
||||
album = self.content['date'] + ' - ' + self.content['title'] + ' (' + self.content['label'] + ' - ' + self.content['catalog'] + ')'
|
||||
return artist, album
|
||||
|
||||
def complete(self):
|
||||
'''Check MetaFile for containing all data'''
|
||||
self.__init__(self.path)
|
||||
for l in self.rawcontent.splitlines():
|
||||
if re.search('^[0-9A-Za-z]+?\t$', l):
|
||||
return False
|
||||
return True
|
||||
|
||||
def count_tracks(self):
|
||||
'''Returns tracks count'''
|
||||
return len(self.content['tracks']) - self.content['tracks'].count(None)
|
||||
|
||||
class WaveFile(File):
|
||||
def __init__(self, path, track_nr=None):
|
||||
File.__init__(self, path)
|
||||
|
||||
self.track_nr = track_nr
|
||||
self.type = WAVE_FILE_TYPE_BY_EXTENSION[self.name_ext]
|
||||
|
||||
def filename(self, ext=None, cmd=False):
|
||||
title = meta.content['tracks'][self.track_nr]['title']
|
||||
|
||||
if ext:
|
||||
if ext[0] != '.':
|
||||
ext = '.' + ext
|
||||
else:
|
||||
ext = self.name_ext
|
||||
|
||||
f_name = int_to_str(self.track_nr) + ' - ' + title + ext
|
||||
|
||||
if cmd:
|
||||
f_name = f_name.replace(' ', '\ ')
|
||||
|
||||
f_name = f_name.replace('!', '')
|
||||
f_name = f_name.replace('?', '')
|
||||
f_name = f_name.replace('/', ';')
|
||||
|
||||
return f_name
|
||||
|
||||
def tag(self):
|
||||
if self.type == 'Free Lossless Audio Codec':
|
||||
f = FLAC(self.name)
|
||||
tags = meta.flac_tags(self.track_nr)
|
||||
for t in tags[0]:
|
||||
f[t] = tags[0][t]
|
||||
f.save()
|
||||
|
||||
def mutagen(self):
|
||||
if self.type == 'Free Lossless Audio Codec':
|
||||
return FLAC(self.name)
|
||||
|
||||
def split(albumpath):
|
||||
|
||||
os.chdir(albumpath)
|
||||
base_dir = Directory(os.getcwd())
|
||||
cue = None
|
||||
wave = None
|
||||
|
||||
# determining correct cue file
|
||||
# if perfect match found
|
||||
for _cue in base_dir.filter('CueFile'):
|
||||
for _wave in base_dir.filter('WaveFile'):
|
||||
if _cue.header['file'] == _wave.name:
|
||||
logger.info('CUE Sheet found: {0}'.format(_cue.name))
|
||||
logger.info('Music file found: {0}'.format(_wave.name))
|
||||
cue = _cue
|
||||
wave = _wave
|
||||
# if no perfect match found then try without extensions
|
||||
if not cue and not wave:
|
||||
logger.info('No match for music files, trying to match without extensions...')
|
||||
for _cue in base_dir.filter('CueFile'):
|
||||
for _wave in base_dir.filter('WaveFile'):
|
||||
if ''.join(os.path.splitext(_cue.header['file'])[:-1]) == _wave.name_name:
|
||||
logger.info('Possible CUE Sheet found: {0}'.format(_cue.name))
|
||||
logger.info('CUE Sheet refers music file: {0}'.format(_cue.header['file']))
|
||||
logger.info('Possible Music file found: {0}'.format(_wave.name))
|
||||
cue = _cue
|
||||
wave = _wave
|
||||
cue.header['file'] = wave.name
|
||||
# if still no match then raise an exception
|
||||
if not cue and not wave:
|
||||
raise ValueError('No music file match found!')
|
||||
|
||||
# Split with xld or shntool
|
||||
splitter = 'shntool'
|
||||
xldprofile = None
|
||||
|
||||
# use xld profile to split cue
|
||||
if headphones.ENCODER == 'xld' and headphones.MUSIC_ENCODER and headphones.XLDPROFILE:
|
||||
import getXldProfile
|
||||
xldprofile, xldformat, _ = getXldProfile.getXldProfile(headphones.XLDPROFILE)
|
||||
if not xldformat:
|
||||
raise ValueError('Details for xld profile "%s" not found, cannot split cue' % (xldprofile))
|
||||
else:
|
||||
if headphones.ENCODERFOLDER:
|
||||
splitter = os.path.join(headphones.ENCODERFOLDER, 'xld')
|
||||
else:
|
||||
splitter = 'xld'
|
||||
# use standard xld command to split cue
|
||||
elif sys.platform == 'darwin':
|
||||
splitter = 'xld'
|
||||
if not check_splitter(splitter):
|
||||
splitter = 'shntool'
|
||||
|
||||
if splitter == 'shntool' and not check_splitter(splitter):
|
||||
raise ValueError('Command not found, ensure shntools with FLAC or xld (OS X) installed')
|
||||
|
||||
# Determine if file can be split (only flac allowed for shntools)
|
||||
if 'xld' in splitter and wave.name_ext not in WAVE_FILE_TYPE_BY_EXTENSION.keys() or \
|
||||
wave.type not in SHNTOOL_COMPATIBLE:
|
||||
raise ValueError('Cannot split, audio file has unsupported extension')
|
||||
|
||||
# generate temporary metafile describing the cue
|
||||
if not base_dir.filter('MetaFile'):
|
||||
with open(ALBUM_META_FILE_NAME, mode='w') as meta_file:
|
||||
meta_file.write(cue.get_meta())
|
||||
base_dir.content.append(MetaFile(os.path.abspath(ALBUM_META_FILE_NAME)))
|
||||
# check metafile for completeness
|
||||
if not base_dir.filter('MetaFile'):
|
||||
raise ValueError('Meta file {0} missing!'.format(ALBUM_META_FILE_NAME))
|
||||
else:
|
||||
global meta
|
||||
meta = base_dir.filter('MetaFile')[0]
|
||||
|
||||
# Split with xld
|
||||
if 'xld' in splitter:
|
||||
cmd = [splitter]
|
||||
cmd.extend([wave.name])
|
||||
cmd.extend(['-c'])
|
||||
cmd.extend([cue.name])
|
||||
if xldprofile:
|
||||
cmd.extend(['--profile'])
|
||||
cmd.extend([xldprofile])
|
||||
else:
|
||||
cmd.extend(['-f'])
|
||||
cmd.extend(['flac'])
|
||||
cmd.extend(['-o'])
|
||||
cmd.extend([base_dir.path])
|
||||
split = split_baby(wave.name, cmd)
|
||||
else:
|
||||
# Split with shntool
|
||||
with open(SPLIT_FILE_NAME, mode='w') as split_file:
|
||||
split_file.write(cue.breakpoints())
|
||||
|
||||
cmd = ['shntool']
|
||||
cmd.extend(['split'])
|
||||
cmd.extend(['-f'])
|
||||
cmd.extend([SPLIT_FILE_NAME])
|
||||
cmd.extend(['-o'])
|
||||
cmd.extend(['flac'])
|
||||
cmd.extend([wave.name])
|
||||
split = split_baby(wave.name, cmd)
|
||||
os.remove(SPLIT_FILE_NAME)
|
||||
base_dir.update()
|
||||
|
||||
# tag FLAC files
|
||||
if split and meta.count_tracks() == len(base_dir.tracks(ext='.flac', split=True)):
|
||||
for t in base_dir.tracks(ext='.flac', split=True):
|
||||
logger.info('Tagging {0}...'.format(t.name))
|
||||
t.tag()
|
||||
|
||||
# rename FLAC files
|
||||
if split and meta.count_tracks() == len(base_dir.tracks(ext='.flac', split=True)):
|
||||
for t in base_dir.tracks(ext='.flac', split=True):
|
||||
if t.name != t.filename():
|
||||
logger.info('Renaming {0} to {1}...'.format(t.name, t.filename()))
|
||||
os.rename(t.name, t.filename())
|
||||
|
||||
os.remove(ALBUM_META_FILE_NAME)
|
||||
|
||||
if not split:
|
||||
raise ValueError('Failed to split, check logs')
|
||||
else:
|
||||
# Rename original file
|
||||
os.rename(wave.name, wave.name + '.original')
|
||||
return True
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ def split_path(f):
|
||||
components = []
|
||||
drive, path = os.path.splitdrive(f)
|
||||
|
||||
# Stip the folder from the path, iterate until nothing is left
|
||||
# Strip the folder from the path, iterate until nothing is left
|
||||
while True:
|
||||
path, folder = os.path.split(path)
|
||||
|
||||
@@ -315,18 +315,9 @@ def extract_data(s):
|
||||
s = s.replace('_', ' ')
|
||||
|
||||
#headphones default format
|
||||
pattern = re.compile(r'(?P<name>.*?)\s\-\s(?P<album>.*?)\s\[(?P<year>.*?)\]', re.VERBOSE)
|
||||
pattern = re.compile(r'(?P<name>.*?)\s\-\s(?P<album>.*?)\s[\[\(](?P<year>.*?)[\]\)]', re.VERBOSE)
|
||||
match = pattern.match(s)
|
||||
|
||||
if match:
|
||||
name = match.group("name")
|
||||
album = match.group("album")
|
||||
year = match.group("year")
|
||||
return (name, album, year)
|
||||
|
||||
#newzbin default format
|
||||
pattern = re.compile(r'(?P<name>.*?)\s\-\s(?P<album>.*?)\s\((?P<year>\d+?\))', re.VERBOSE)
|
||||
match = pattern.match(s)
|
||||
if match:
|
||||
name = match.group("name")
|
||||
album = match.group("album")
|
||||
@@ -444,6 +435,75 @@ def extract_metadata(f):
|
||||
|
||||
return (None, None, None)
|
||||
|
||||
def get_downloaded_track_list(albumpath):
|
||||
"""
|
||||
Return a list of audio files for the given directory.
|
||||
"""
|
||||
downloaded_track_list = []
|
||||
|
||||
for root, dirs, files in os.walk(albumpath):
|
||||
for _file in files:
|
||||
extension = os.path.splitext(_file)[1].lower()[1:]
|
||||
if extension in headphones.MEDIA_FORMATS:
|
||||
downloaded_track_list.append(os.path.join(root, _file))
|
||||
|
||||
return downloaded_track_list
|
||||
|
||||
def preserve_torrent_direcory(albumpath):
|
||||
"""
|
||||
Copy torrent directory to headphones-modified to keep files for seeding.
|
||||
"""
|
||||
from headphones import logger
|
||||
new_folder = os.path.join(albumpath, 'headphones-modified'.encode(headphones.SYS_ENCODING, 'replace'))
|
||||
logger.info("Copying files to 'headphones-modified' subfolder to preserve downloaded files for seeding")
|
||||
try:
|
||||
shutil.copytree(albumpath, new_folder)
|
||||
return new_folder
|
||||
except Exception, e:
|
||||
logger.warn("Cannot copy/move files to temp folder: " + \
|
||||
new_folder.decode(headphones.SYS_ENCODING, 'replace') + \
|
||||
". Not continuing. Error: " + str(e))
|
||||
return None
|
||||
|
||||
def cue_split(albumpath):
|
||||
"""
|
||||
Attempts to check and split audio files by a cue for the given directory.
|
||||
"""
|
||||
# Walk directory and scan all media files
|
||||
count = 0
|
||||
cue_count = 0
|
||||
cue_dirs = []
|
||||
|
||||
for root, dirs, files in os.walk(albumpath):
|
||||
for _file in files:
|
||||
extension = os.path.splitext(_file)[1].lower()[1:]
|
||||
if extension in headphones.MEDIA_FORMATS:
|
||||
count += 1
|
||||
elif extension == 'cue':
|
||||
cue_count += 1
|
||||
if root not in cue_dirs:
|
||||
cue_dirs.append(root)
|
||||
|
||||
# Split cue
|
||||
if cue_count and cue_count >= count and cue_dirs:
|
||||
|
||||
from headphones import logger, cuesplit
|
||||
logger.info("Attempting to split audio files by cue")
|
||||
|
||||
cwd = os.getcwd()
|
||||
for cue_dir in cue_dirs:
|
||||
try:
|
||||
cuesplit.split(cue_dir)
|
||||
except Exception, e:
|
||||
os.chdir(cwd)
|
||||
logger.warn("Cue not split: " + str(e))
|
||||
return False
|
||||
|
||||
os.chdir(cwd)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def extract_logline(s):
|
||||
# Default log format
|
||||
pattern = re.compile(r'(?P<timestamp>.*?)\s\-\s(?P<level>.*?)\s*\:\:\s(?P<thread>.*?)\s\:\s(?P<message>.*)', re.VERBOSE)
|
||||
|
||||
@@ -618,7 +618,7 @@ def addReleaseById(rid, rgid=None):
|
||||
newValueDict = {"ArtistID": release_dict['artist_id'],
|
||||
"ReleaseID": rgid,
|
||||
"ArtistName": release_dict['artist_name'],
|
||||
"AlbumTitle": release_dict['rg_title'],
|
||||
"AlbumTitle": release_dict['title'] if 'title' in release_dict else release_dict['rg_title'],
|
||||
"AlbumASIN": release_dict['asin'],
|
||||
"ReleaseDate": release_dict['date'],
|
||||
"DateAdded": helpers.today(),
|
||||
|
||||
82
headphones/postprocessor.py
Normal file → Executable file
82
headphones/postprocessor.py
Normal file → Executable file
@@ -178,66 +178,18 @@ def verify(albumid, albumpath, Kind=None, forced=False):
|
||||
logger.info("Looks like " + os.path.basename(albumpath).decode(headphones.SYS_ENCODING, 'replace') + " isn't complete yet. Will try again on the next run")
|
||||
return
|
||||
|
||||
|
||||
# use xld to split cue
|
||||
|
||||
if headphones.CONFIG.ENCODER == 'xld' and headphones.CONFIG.MUSIC_ENCODER and downloaded_cuecount and downloaded_cuecount >= len(downloaded_track_list):
|
||||
|
||||
import getXldProfile
|
||||
|
||||
(xldProfile, xldFormat, xldBitrate) = getXldProfile.getXldProfile(headphones.CONFIG.XLDPROFILE)
|
||||
if not xldFormat:
|
||||
logger.info(u'Details for xld profile "%s" not found, cannot split cue' % (xldProfile))
|
||||
# Split cue
|
||||
if downloaded_cuecount and downloaded_cuecount >= len(downloaded_track_list):
|
||||
if headphones.CONFIG.KEEP_TORRENT_FILES and Kind=="torrent":
|
||||
albumpath = helpers.preserve_torrent_direcory(albumpath)
|
||||
if albumpath and helpers.cue_split(albumpath):
|
||||
downloaded_track_list = helpers.get_downloaded_track_list(albumpath)
|
||||
else:
|
||||
if headphones.CONFIG.ENCODERFOLDER:
|
||||
xldencoder = os.path.join(headphones.CONFIG.ENCODERFOLDER, 'xld')
|
||||
else:
|
||||
xldencoder = os.path.join('/Applications','xld')
|
||||
|
||||
for r,d,f in os.walk(albumpath):
|
||||
xldfolder = r
|
||||
xldfile = ''
|
||||
xldcue = ''
|
||||
for file in f:
|
||||
if any(file.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS) and not xldfile:
|
||||
xldfile = os.path.join(r, file)
|
||||
elif file.lower().endswith('.cue') and not xldcue:
|
||||
xldcue = os.path.join(r, file)
|
||||
|
||||
if xldfile and xldcue and xldfolder:
|
||||
xldcmd = xldencoder
|
||||
xldcmd = xldcmd + ' "' + xldfile + '"'
|
||||
xldcmd = xldcmd + ' -c'
|
||||
xldcmd = xldcmd + ' "' + xldcue + '"'
|
||||
xldcmd = xldcmd + ' --profile'
|
||||
xldcmd = xldcmd + ' "' + xldProfile + '"'
|
||||
xldcmd = xldcmd + ' -o'
|
||||
xldcmd = xldcmd + ' "' + xldfolder + '"'
|
||||
logger.info(u"Cue found, splitting file " + xldfile.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
logger.debug(xldcmd)
|
||||
os.system(xldcmd)
|
||||
|
||||
# count files, should now be more than original if xld successfully split
|
||||
|
||||
new_downloaded_track_list_count = 0
|
||||
for r,d,f in os.walk(albumpath):
|
||||
for file in f:
|
||||
if any(file.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
|
||||
new_downloaded_track_list_count += 1
|
||||
|
||||
if new_downloaded_track_list_count > len(downloaded_track_list):
|
||||
|
||||
# rename original unsplit files
|
||||
for downloaded_track in downloaded_track_list:
|
||||
os.rename(downloaded_track, downloaded_track + '.original')
|
||||
|
||||
#reload
|
||||
|
||||
downloaded_track_list = []
|
||||
for r,d,f in os.walk(albumpath):
|
||||
for file in f:
|
||||
if any(file.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
|
||||
downloaded_track_list.append(os.path.join(r, file))
|
||||
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)
|
||||
return
|
||||
|
||||
# test #1: metadata - usually works
|
||||
logger.debug('Verifying metadata...')
|
||||
@@ -328,7 +280,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
|
||||
|
||||
logger.info('Starting post-processing for: %s - %s' % (release['ArtistName'], release['AlbumTitle']))
|
||||
# Check to see if we're preserving the torrent dir
|
||||
if headphones.CONFIG.KEEP_TORRENT_FILES and Kind=="torrent":
|
||||
if headphones.CONFIG.KEEP_TORRENT_FILES and Kind=="torrent" and 'headphones-modified' not in albumpath:
|
||||
new_folder = os.path.join(albumpath, 'headphones-modified'.encode(headphones.SYS_ENCODING, 'replace'))
|
||||
logger.info("Copying files to 'headphones-modified' subfolder to preserve downloaded files for seeding")
|
||||
try:
|
||||
@@ -343,14 +295,11 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
|
||||
# Could probably just throw in the "headphones-modified" folder,
|
||||
# but this is good to make sure we're not counting files that may have failed to move
|
||||
downloaded_track_list = []
|
||||
downloaded_cuecount = 0
|
||||
|
||||
for r,d,f in os.walk(albumpath):
|
||||
for files in f:
|
||||
if any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
|
||||
downloaded_track_list.append(os.path.join(r, files))
|
||||
elif files.lower().endswith('.cue'):
|
||||
downloaded_cuecount += 1
|
||||
|
||||
# Check if files are valid media files and are writeable, before the steps
|
||||
# below are executed. This simplifies errors and prevents unfinished steps.
|
||||
@@ -1179,6 +1128,13 @@ def forcePostProcess(dir=None, expand_subfolders=True, album_dir=None):
|
||||
except Exception as e:
|
||||
name = album = year = None
|
||||
|
||||
# Check if there's a cue to split
|
||||
if not name and not album and helpers.cue_split(folder):
|
||||
try:
|
||||
name, album, year = helpers.extract_metadata(folder)
|
||||
except Exception as e:
|
||||
name = album = year = None
|
||||
|
||||
if name and album:
|
||||
release = myDB.action('SELECT AlbumID, ArtistName, AlbumTitle from albums WHERE ArtistName LIKE ? and AlbumTitle LIKE ?', [name, album]).fetchone()
|
||||
if release:
|
||||
|
||||
@@ -185,6 +185,9 @@ class WebInterface(object):
|
||||
myDB.action('DELETE from allalbums WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']])
|
||||
myDB.action('DELETE from alltracks WHERE ArtistID=? AND AlbumID=?', [ArtistID, album['AlbumID']])
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [album['AlbumID']])
|
||||
from headphones import cache
|
||||
c = cache.Cache()
|
||||
c.remove_from_cache(AlbumID=album['AlbumID'])
|
||||
importer.finalize_update(ArtistID, ArtistName)
|
||||
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
|
||||
removeExtras.exposed = True
|
||||
@@ -207,7 +210,7 @@ class WebInterface(object):
|
||||
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
|
||||
resumeArtist.exposed = True
|
||||
|
||||
def deleteArtist(self, ArtistID):
|
||||
def removeArtist(self, ArtistID):
|
||||
logger.info(u"Deleting all traces of artist: " + ArtistID)
|
||||
myDB = db.DBConnection()
|
||||
namecheck = myDB.select('SELECT ArtistName from artists where ArtistID=?', [ArtistID])
|
||||
@@ -215,23 +218,29 @@ class WebInterface(object):
|
||||
artistname=name['ArtistName']
|
||||
myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID])
|
||||
|
||||
rgids = myDB.select('SELECT DISTINCT ReleaseGroupID FROM albums JOIN releases ON AlbumID = ReleaseGroupID WHERE ArtistID=?', [ArtistID])
|
||||
from headphones import cache
|
||||
c = cache.Cache()
|
||||
|
||||
rgids = myDB.select('SELECT AlbumID FROM albums WHERE ArtistID=? UNION SELECT AlbumID FROM allalbums WHERE ArtistID=?', [ArtistID, ArtistID])
|
||||
for rgid in rgids:
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rgid['ReleaseGroupID']])
|
||||
myDB.action('DELETE from have WHERE Matched=?', [rgid['ReleaseGroupID']])
|
||||
albumid = rgid['AlbumID']
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [albumid])
|
||||
myDB.action('DELETE from have WHERE Matched=?', [albumid])
|
||||
c.remove_from_cache(AlbumID=albumid)
|
||||
myDB.action('DELETE from descriptions WHERE ReleaseGroupID=?', [albumid])
|
||||
|
||||
myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID])
|
||||
|
||||
rgids = myDB.select('SELECT DISTINCT ReleaseGroupID FROM allalbums JOIN releases ON AlbumID = ReleaseGroupID WHERE ArtistID=?', [ArtistID])
|
||||
for rgid in rgids:
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rgid['ReleaseGroupID']])
|
||||
myDB.action('DELETE from have WHERE Matched=?', [rgid['ReleaseGroupID']])
|
||||
|
||||
myDB.action('DELETE from allalbums WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from alltracks WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from have WHERE ArtistName=?', [artistname])
|
||||
c.remove_from_cache(ArtistID=ArtistID)
|
||||
myDB.action('DELETE from descriptions WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID])
|
||||
|
||||
def deleteArtist(self, ArtistID):
|
||||
self.removeArtist(ArtistID)
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
deleteArtist.exposed = True
|
||||
|
||||
@@ -240,23 +249,7 @@ class WebInterface(object):
|
||||
myDB = db.DBConnection()
|
||||
emptyArtistIDs = [row['ArtistID'] for row in myDB.select("SELECT ArtistID FROM artists WHERE LatestAlbum IS NULL")]
|
||||
for ArtistID in emptyArtistIDs:
|
||||
logger.info(u"Deleting all traces of artist: " + ArtistID)
|
||||
myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID])
|
||||
|
||||
rgids = myDB.select('SELECT DISTINCT ReleaseGroupID FROM albums JOIN releases ON AlbumID = ReleaseGroupID WHERE ArtistID=?', [ArtistID])
|
||||
for rgid in rgids:
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rgid['ReleaseGroupID']])
|
||||
|
||||
myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID])
|
||||
|
||||
rgids = myDB.select('SELECT DISTINCT ReleaseGroupID FROM allalbums JOIN releases ON AlbumID = ReleaseGroupID WHERE ArtistID=?', [ArtistID])
|
||||
for rgid in rgids:
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rgid['ReleaseGroupID']])
|
||||
|
||||
myDB.action('DELETE from allalbums WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from alltracks WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID])
|
||||
self.removeArtist(ArtistID)
|
||||
deleteEmptyArtists.exposed = True
|
||||
|
||||
def refreshArtist(self, ArtistID):
|
||||
@@ -388,6 +381,12 @@ class WebInterface(object):
|
||||
myDB.action('DELETE from allalbums WHERE AlbumID=?', [AlbumID])
|
||||
myDB.action('DELETE from alltracks WHERE AlbumID=?', [AlbumID])
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [AlbumID])
|
||||
myDB.action('DELETE from descriptions WHERE ReleaseGroupID=?', [AlbumID])
|
||||
|
||||
from headphones import cache
|
||||
c = cache.Cache()
|
||||
c.remove_from_cache(AlbumID=AlbumID)
|
||||
|
||||
if ArtistID:
|
||||
raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
|
||||
else:
|
||||
@@ -641,22 +640,7 @@ class WebInterface(object):
|
||||
artistsToAdd = []
|
||||
for ArtistID in args:
|
||||
if action == 'delete':
|
||||
myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID])
|
||||
|
||||
rgids = myDB.select('SELECT DISTINCT ReleaseGroupID FROM albums JOIN releases ON AlbumID = ReleaseGroupID WHERE ArtistID=?', [ArtistID])
|
||||
for rgid in rgids:
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rgid['ReleaseGroupID']])
|
||||
|
||||
myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID])
|
||||
|
||||
rgids = myDB.select('SELECT DISTINCT ReleaseGroupID FROM allalbums JOIN releases ON AlbumID = ReleaseGroupID WHERE ArtistID=?', [ArtistID])
|
||||
for rgid in rgids:
|
||||
myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rgid['ReleaseGroupID']])
|
||||
|
||||
myDB.action('DELETE from allalbums WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('DELETE from alltracks WHERE ArtistID=?', [ArtistID])
|
||||
myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID])
|
||||
self.removeArtist(ArtistID)
|
||||
elif action == 'pause':
|
||||
controlValueDict = {'ArtistID': ArtistID}
|
||||
newValueDict = {'Status': 'Paused'}
|
||||
|
||||
Reference in New Issue
Block a user