def track(self, slug): track = library_dao.get_track_by_slug(slug) if track is None: raise cherrypy.NotFound() remotes.update_track(track) remotes_album = remotes_artist = None if track.album is not None: remotes.update_album(track.album) remotes_album = remotes.get_album(track.album) if track.artist is not None: remotes.update_artist(track.artist) remotes_artist = remotes.get_artist(track.artist) remotes_track = remotes.get_track(track) if track.artist is not None: artist_listened_tuples = self.get_artist_listened_tuples(track.artist.name) if track.album is not None: album_listened_tuples = self.get_album_listened_tuples(track.artist.name, track.album.name) else: album_listened_tuples = None else: artist_listened_tuples = None album_listened_tuples = None return { 'track': track, 'remotes_artist': remotes_artist, 'remotes_album': remotes_album, 'remotes_track': remotes_track, 'album_listened_tuples': album_listened_tuples, 'artist_listened_tuples': artist_listened_tuples, }
def album(self, artist_slug, album_slug): try: album = (get_database().query(Album) # _dir_tracks() uses paths .options(joinedload(Album.tracks, Track.paths)) .filter_by(slug=album_slug).one()) except NoResultFound: raise cherrypy.NotFound() # always update seen, True means now album.seen = True remotes.update_album(album) remotes_artists = [] for artist in album.artists: remotes.update_artist(artist) remotes_artist = remotes.get_artist(artist) if remotes_artist is not None: remotes_artists.append(remotes_artist) disc_nos = {} for track in album.tracks: remotes.update_track(track) disc = '' if track.disc is None else track.disc if disc not in disc_nos: disc_nos[disc] = [] if track.number is not None: # extract max track no, '01' will become 1, '01/10' will become 10 disc_nos[disc].append(int(re.search('\d+', track.number).group())) else: disc_nos[disc].append(None) album_disc_nos = [] for disc, numbers in disc_nos.items(): max_number = max([number for number in numbers if number is not None], default=None) if max_number is not None: album_disc_nos.append((disc, len(numbers), max_number)) album_disc_nos = sorted(album_disc_nos, key=lambda album_disc_no: album_disc_no[0]) remotes_album = remotes.get_album(album) dir_tracks = self._dir_tracks(album.tracks) if len(album.artists) > 0: artist_name = album.artists[0].name else: artist_name = None album_listened_tuples = self.get_album_listened_tuples(artist_name, album.name) artist_listened_tuples = self.get_artist_listened_tuples(artist_name) return { 'album_disc_nos': album_disc_nos, 'album': album, 'dir_tracks': dir_tracks, 'remotes_artists': remotes_artists, 'remotes_album': remotes_album, 'album_listened_tuples': album_listened_tuples, 'artist_listened_tuples': artist_listened_tuples, }
def add(self, archive_password=None, audio_file=None, session=None, artist_name_fallback=None): cache_key = LibraryUpload.CACHE_KEY % (cherrypy.request.user.id, session) all_tracks = None if not cache.has(cache_key): raise cherrypy.HTTPError(status=409) else: all_tracks = cache.get(cache_key) if audio_file is not None and len(audio_file) == 0: audio_file = None if archive_password is not None and len(archive_password) == 0: archive_password = None content_disposition = cherrypy.request.headers.get('content-disposition') filename = content_disposition[content_disposition.index('filename=') + 9:] if filename.startswith('"') and filename.endswith('"'): filename = filename[1:-1] filename = unquote(filename) ext = os.path.splitext(filename)[1].lower()[1:] basename = os.path.splitext(filename)[0] cache_path = os.path.join(cherrypy.config['opmuse'].get('cache.path'), 'upload') if not os.path.exists(cache_path): os.mkdir(cache_path) tempdir = tempfile.mkdtemp(dir=cache_path) path = os.path.join(tempdir, filename) paths = [] rarfile.PATH_SEP = '/' messages = [] with open(path, 'wb') as fileobj: fileobj.write(cherrypy.request.rfile.read()) # this file is a regular file that belongs to an audio_file if audio_file is not None: track = None tries = 0 # try and sleep until we get the track.. this will almost always # be needed because of the async upload. while track is None and tries < 10: track = library_dao.get_track_by_filename(audio_file.encode('utf8')) tries += 1 if track is None: time.sleep(3) if track is None: messages.append(('warning', ("<strong>%s</strong>: Skipping <strong>%s</strong>, timeout trying to " + "find its track.") % (audio_file, filename))) else: track_structure = TrackStructureParser(track) track_path = track_structure.get_path(absolute=True) relative_track_path = track_structure.get_path(absolute=False).decode('utf8', 'replace') new_path = os.path.join(track_path, filename.encode('utf8')) if os.path.exists(new_path): messages.append(('warning', ("<strong>%s</strong>: Skipping <strong>%s</strong>, already exists " + "in <strong>%s</strong>.") % (audio_file, filename, relative_track_path))) else: shutil.move(path.encode('utf8'), new_path) messages.append(('info', ("<strong>%s</strong>: Uploaded <strong>%s</strong> to " + "<strong>%s</strong>.") % (audio_file, filename, relative_track_path))) elif ext == "zip": # set artist name fallback to zip's name so if it's missing artist tags # it's easily distinguishable and editable so it can be fixed after upload. artist_name_fallback = basename try: zip = ZipFile(path) if archive_password is not None: zip.setpassword(archive_password.encode()) zip.extractall(tempdir) os.remove(path) for name in zip.namelist(): namepath = os.path.join(tempdir, name) # ignore hidden files, e.g. OSX archive weirdness and such if name.startswith(".") or os.path.split(name)[0] == "__MACOSX": shutil.rmtree(namepath) continue paths.append(namepath.encode('utf8')) except Exception as error: messages.append(('danger', "<strong>%s</strong>: %s" % (os.path.basename(path), error))) elif ext == "rar": # look at corresponding ext == zip comment... artist_name_fallback = basename try: rar = RarFile(path) if archive_password is None and rar.needs_password(): messages.append(('danger', "<strong>%s</strong>: Needs password but none provided." % os.path.basename(path))) else: if archive_password is not None: rar.setpassword(archive_password) rar.extractall(tempdir) os.remove(path) for name in rar.namelist(): namepath = os.path.join(tempdir, name) if name.startswith("."): shutil.rmtree(namepath) continue paths.append(namepath.encode('utf8')) except Exception as error: messages.append(('danger', "<strong>%s</strong>: %s" % (os.path.basename(path), error))) # this is a plain audio file else: paths.append(path.encode('utf8')) for path in paths: # update modified time to now, we don't want the time from the zip # archive or whatever os.utime(path, None) if len(paths) > 0: tracks, add_files_messages = library_dao.add_files(paths, move=True, remove_dirs=False, artist_name_fallback=artist_name_fallback, user=cherrypy.request.user) messages += add_files_messages else: tracks = [] shutil.rmtree(tempdir) for track in tracks: all_tracks.append(track.id) if track.album is not None: remotes.update_album(track.album) if track.artist is not None: remotes.update_artist(track.artist) remotes.update_track(track) hierarchy = Library._produce_track_hierarchy(library_dao.get_tracks_by_ids(all_tracks)) return {'hierarchy': hierarchy, 'messages': messages}
def default(self, query=None, type=None): artists = [] albums = [] tracks = [] track_ids = [] hierarchy = None album_track_ids = set() recent_searches = [] if query is not None: albums = None tracks = None # only search for artists if type == 'artist': artists = search.query_artist(query, exact=True) albums = [] tracks = [] else: artists = search.query_artist(query) if albums is None: albums = search.query_album(query) if tracks is None: tracks = search.query_track(query) for artist in artists: remotes.update_artist(artist) for album in albums: remotes.update_album(album) for track in tracks: track_ids.append(track.id) remotes.update_track(track) entities = artists + albums + tracks if len(entities) == 1: for artist in artists: raise HTTPRedirect('/%s' % artist.slug) for album in albums: raise HTTPRedirect('/%s/%s' % (album.artists[0].slug, album.slug)) for track in tracks: raise HTTPRedirect('/library/track/%s' % track.slug) if cache.has(Search.CACHE_RECENT_KEY): recent_searches = cache.get(Search.CACHE_RECENT_KEY) else: cache.set(Search.CACHE_RECENT_KEY, recent_searches) if type is None and len(entities) > 0: if len(recent_searches) == 0 or query != recent_searches[0][0]: recent_searches.insert(0, (query, datetime.datetime.now(), cherrypy.request.user.login)) if len(recent_searches) > Search.MAX_RECENT: recent_searches.pop() entities = sorted(entities, key=lambda entity: entity._SEARCH_SCORE, reverse=True) hierarchy = Library._produce_track_hierarchy(entities) for key, result_artist in hierarchy['artists'].items(): for key, result_album in result_artist['albums'].items(): for track_id in library_dao.get_track_ids_by_album_id(result_album['entity'].id): album_track_ids.add(track_id) return { 'query': query, 'hierarchy': hierarchy, 'tracks': tracks, 'albums': albums, 'artists': artists, 'track_ids': track_ids, 'album_track_ids': list(album_track_ids), 'recent_searches': recent_searches }