diff options
-rw-r--r-- | Makefile | 36 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | backend/zmusic/database.py | 37 | ||||
-rw-r--r-- | backend/zmusic/endpoints/error.py | 4 | ||||
-rw-r--r-- | backend/zmusic/endpoints/login.py | 2 | ||||
-rw-r--r-- | backend/zmusic/endpoints/scan.py | 12 | ||||
-rw-r--r-- | backend/zmusic/endpoints/zip.py | 73 | ||||
-rw-r--r-- | backend/zmusic/login.py | 4 | ||||
-rw-r--r-- | backend/zmusic/picard/file.py | 4 | ||||
-rw-r--r-- | backend/zmusic/picard/formats/apev2.py | 6 | ||||
-rw-r--r-- | backend/zmusic/picard/formats/asf.py | 4 | ||||
-rw-r--r-- | backend/zmusic/picard/formats/id3.py | 22 | ||||
-rw-r--r-- | backend/zmusic/picard/formats/mp4.py | 17 | ||||
-rw-r--r-- | backend/zmusic/picard/formats/mutagenext/compatid3.py | 6 | ||||
-rw-r--r-- | backend/zmusic/picard/formats/vorbis.py | 6 | ||||
-rw-r--r-- | backend/zmusic/picard/metadata.py | 8 | ||||
-rw-r--r-- | backend/zmusic/picard/util/__init__.py | 23 | ||||
-rw-r--r-- | backend/zmusic/streams.py | 4 | ||||
-rw-r--r-- | server.cfg | 7 |
19 files changed, 121 insertions, 161 deletions
@@ -16,34 +16,22 @@ deploy: all @ssh $(SSH_OPTS) -Nf $(WEB_SERVER) @echo " RSYNC frontend/ $(WEB_SERVER):$(SERVER_STATIC_PATH)" - @ssh -t $(SSH_OPTS) $(WEB_SERVER) "sudo -u $(SERVER_STATIC_USER) -v" - @rsync -aizm --delete-excluded --exclude=Makefile --exclude=*.swp --exclude=bin/ --exclude=Makefile \ - --include=scripts.min.js --include=styles.min.css --exclude=*.js --exclude=*.css --rsh="ssh $(SSH_OPTS)" \ - --rsync-path="sudo -n -u $(SERVER_STATIC_USER) rsync" frontend/ "$(WEB_SERVER):$(SERVER_STATIC_PATH)" + @rsync -rltizm --delete-excluded --exclude=Makefile --exclude=*.swp --exclude=bin/ --exclude=Makefile \ + --include=scripts.min.js --include=styles.min.css --exclude=*.js --exclude=*.css \ + --rsh="ssh $(SSH_OPTS)" frontend/ "$(WEB_SERVER):$(SERVER_STATIC_PATH)" - @echo " CHOWN $(SERVER_STATIC_USER):$(SERVER_APP_USER) $(WEB_SERVER):$(SERVER_STATIC_PATH)" - @ssh -t $(SSH_OPTS) $(WEB_SERVER) "sudo chown -R $(SERVER_STATIC_USER):$(SERVER_APP_USER) '$(SERVER_STATIC_PATH)'" + @echo " RSYNC backend/zmusic/ $(WEB_SERVER):$(SERVER_APP_PATH)" + @rsync -rltizm --delete-excluded --exclude=*.swp --exclude=*.pyc --filter="P app.cfg" \ + --rsh="ssh $(SSH_OPTS)" backend/zmusic/ "$(WEB_SERVER):$(SERVER_APP_PATH)" - @echo " RSYNC backend/zmusic $(WEB_SERVER):$(SERVER_APP_PATH)" - @ssh -t $(SSH_OPTS) $(WEB_SERVER) "sudo -u $(SERVER_APP_USER) -v" - @rsync -aizm --delete-excluded --filter="P zmusic.db" --filter="P app.cfg" --exclude=*.swp --exclude=*.pyc \ - --rsh="ssh $(SSH_OPTS)" --rsync-path="sudo -n -u $(SERVER_APP_USER) rsync" backend/zmusic/ "$(WEB_SERVER):$(SERVER_APP_PATH)" - - @echo " CHOWN $(SERVER_APP_USER):$(SERVER_APP_USER) $(WEB_SERVER):$(SERVER_APP_PATH)" - @ssh -t $(SSH_OPTS) $(WEB_SERVER) "sudo chown -R $(SERVER_APP_USER):$(SERVER_APP_USER) '$(SERVER_APP_PATH)'" - - @echo " CHMOD 750/640 $(WEB_SERVER):$(SERVER_APP_PATH) $(WEB_SERVER):$(SERVER_STATIC_PATH)" - @ssh -t $(SSH_OPTS) $(WEB_SERVER) "sudo find '$(SERVER_APP_PATH)' '$(SERVER_STATIC_PATH)' -type f -exec chmod 640 {} \;; \ - sudo find '$(SERVER_APP_PATH)' '$(SERVER_STATIC_PATH)' -type d -exec chmod 750 {} \;;" - - @echo " UWSGI restart $(WEB_SERVER)" - @ssh -t $(SSH_OPTS) $(WEB_SERVER) "sudo /etc/init.d/uwsgi restart" + @echo " UWSGI restart" + @ssh $(SSH_OPTS) $(WEB_SERVER) "systemctl restart uwsgi@emperor.service" @echo " SSH $(WEB_SERVER)" @ssh -O exit $(SSH_OPTS) $(WEB_SERVER) update-collection: - @echo " RSYNC $(LOCAL_COLLECTION_PATH) $(UPLOAD_SERVER):$(UPLOAD_SERVER_PATH)" - @rsync -avzPi --delete-excluded --delete-after --fuzzy --exclude=.directory '$(LOCAL_COLLECTION_PATH)/' '$(UPLOAD_SERVER):$(UPLOAD_SERVER_PATH)' - @echo " SCAN $(WEB_SERVER)" - @curl -L 'https://$(WEB_SERVER)/scan?username=$(ADMIN_USERNAME)&password=$(ADMIN_PASSWORD)' + @echo " RSYNC $(LOCAL_COLLECTION_PATH) $(WEB_SERVER):$(SERVER_MUSIC_PATH)" + @rsync -avziP --delete-excluded --delete-after --fuzzy --exclude=.directory '$(LOCAL_COLLECTION_PATH)/' '$(WEB_SERVER):$(SERVER_MUSIC_PATH)' + @echo " SCAN $(URL_BASE)" + @curl -L '$(URL_BASE)/scan?username=$(ADMIN_USERNAME)&password=$(ADMIN_PASSWORD)' @@ -40,7 +40,7 @@ All frontend dependencies are included in the source. * [Flask-Login](http://packages.python.org/Flask-Login/) (`pip install Flask-Login`) * [Mutagen](https://code.google.com/p/mutagen/) (`pip install mutagen`) * [ffmpeg](http://ffmpeg.org/) (`apt-get install ffmpeg`) -* [Python](http://python.org/) >= 2.7 (`apt-get install python`) +* [Python](http://python.org/) >= 3.4 (`apt-get install python`) All backend dependencies must be present on system. @@ -194,9 +194,8 @@ For easy deployment, the makefile has some deployment targets, which are configu * `WEB_SERVER`: The hostname of the deployed server. * `SERVER_STATIC_PATH`: The path of the static frontend files. -* `SERVER_STATIC_USER`: The primary user for static file permissions. * `SERVER_APP_PATH`: The path of the python backend files. -* `SERVER_STATIC_USER`: The primary user of the python backup files. +* `URL_BASE`: The base URL of where to access this on the Internet. These makefile targets should be used with care, and the makefile itself should be inspected to ensure all commands are correct for custom configurations. @@ -245,7 +244,7 @@ The author does not condone or promote using this software for redistributing co ## License -Copyright (C) 2013 Jason A. Donenfeld. All Rights Reserved. +Copyright (C) 2013-2021 Jason A. Donenfeld. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/backend/zmusic/database.py b/backend/zmusic/database.py index 6935a45..8050ab8 100644 --- a/backend/zmusic/database.py +++ b/backend/zmusic/database.py @@ -2,24 +2,15 @@ from zmusic import db from uuid import uuid4 import time -# This is indeed ugly, but it was suggested by the SQLAlchemy documention -# verbatim as a way of dealing with str-centric apps. -class CoerceUTF8(db.TypeDecorator): - impl = db.Unicode - def process_bind_param(self, value, dialect): - if isinstance(value, str): - value = value.decode('utf-8') - return value - class Song(db.Model): __tablename__ = 'songs' - filename = db.Column(CoerceUTF8, primary_key=True) - id = db.Column(CoerceUTF8, nullable=False, index=True) - title = db.Column(CoerceUTF8) - album = db.Column(CoerceUTF8) - artist = db.Column(CoerceUTF8) - mimetype = db.Column(CoerceUTF8) + filename = db.Column(db.Unicode, primary_key=True) + id = db.Column(db.Unicode, nullable=False, index=True) + title = db.Column(db.Unicode) + album = db.Column(db.Unicode) + artist = db.Column(db.Unicode) + mimetype = db.Column(db.Unicode) year = db.Column(db.Integer) track = db.Column(db.Integer) disc = db.Column(db.Integer) @@ -58,15 +49,15 @@ class Song(db.Model): class Download(db.Model): __tablename__ = 'downloads' - id = db.Column(CoerceUTF8, primary_key=True) - leader_id = db.Column(CoerceUTF8) + id = db.Column(db.Unicode, primary_key=True) + leader_id = db.Column(db.Unicode) time = db.Column(db.Integer) - ip = db.Column(CoerceUTF8, index=True) - useragent = db.Column(CoerceUTF8) - song_id = db.Column(CoerceUTF8, db.ForeignKey(Song.id)) - artist = db.Column(CoerceUTF8) - album = db.Column(CoerceUTF8) - title = db.Column(CoerceUTF8) + ip = db.Column(db.Unicode, index=True) + useragent = db.Column(db.Unicode) + song_id = db.Column(db.Unicode, db.ForeignKey(Song.id)) + artist = db.Column(db.Unicode) + album = db.Column(db.Unicode) + title = db.Column(db.Unicode) is_zip = db.Column(db.Boolean) def __init__(self, song, request): diff --git a/backend/zmusic/endpoints/error.py b/backend/zmusic/endpoints/error.py index 74e7cd2..de43caf 100644 --- a/backend/zmusic/endpoints/error.py +++ b/backend/zmusic/endpoints/error.py @@ -9,5 +9,5 @@ def json_error(ex): response.status_code = status_code return response -for code in default_exceptions.iterkeys(): - app.error_handler_spec[None][code] = json_error +for code in default_exceptions.keys(): + app.register_error_handler(code, json_error) diff --git a/backend/zmusic/endpoints/login.py b/backend/zmusic/endpoints/login.py index fdf481d..fd7cde2 100644 --- a/backend/zmusic/endpoints/login.py +++ b/backend/zmusic/endpoints/login.py @@ -16,7 +16,7 @@ def login(): @app.route('/login', methods=['GET']) def login_check(): - response = jsonify(loggedin=current_user.is_authenticated()) + response = jsonify(loggedin=current_user.is_authenticated) response.cache_control.no_cache = True return response diff --git a/backend/zmusic/endpoints/scan.py b/backend/zmusic/endpoints/scan.py index 275c7a8..d1a3078 100644 --- a/backend/zmusic/endpoints/scan.py +++ b/backend/zmusic/endpoints/scan.py @@ -14,11 +14,11 @@ def scan_music(): def do_scan(): yield "%i | Begin.\n" % int(time.time()) all_files = {} - for root, dirs, files in os.walk(unicode(app.config["MUSIC_PATH"])): + for root, dirs, files in os.walk(app.config["MUSIC_PATH"]): if len(files) != 0: - yield "%i | Scanning [%s].\n" % (int(time.time()), encode_filename(root)) + yield "%i | Scanning [%s].\n" % (int(time.time()), root) for name in files: - name = encode_filename(os.path.join(root, name)) + name = os.path.join(root, name) all_files[name] = True song = Song.query.get(name) if song != None: @@ -36,14 +36,14 @@ def scan_music(): continue song.sync_picard(tags) db.session.add(song) - yield "%i | Adding [%s].\n" % (int(time.time()), encode_filename(song.filename)) + yield "%i | Adding [%s].\n" % (int(time.time()), song.filename) for song in db.session.query(Song.filename): if song.filename not in all_files: Song.query.filter(Song.filename == song.filename).delete(False) - yield "%i | Removing [%s].\n" % (int(time.time()), encode_filename(song.filename)) + yield "%i | Removing [%s].\n" % (int(time.time()), song.filename) db.session.commit() yield "%i | Done.\n" % int(time.time()) - response = Response(do_scan(), mimetype="text/plain", direct_passthrough=True) + response = Response(do_scan(), mimetype="text/plain") response.headers.add("X-Accel-Buffering", "no") response.cache_control.no_cache = True return response diff --git a/backend/zmusic/endpoints/zip.py b/backend/zmusic/endpoints/zip.py index e281f6f..d2e4d45 100644 --- a/backend/zmusic/endpoints/zip.py +++ b/backend/zmusic/endpoints/zip.py @@ -36,7 +36,7 @@ def zipfile(): db.session.commit() def do_zip(): - central_directory = "" + central_directory = b"" offset = 0 count = 0 for song in songs: @@ -48,12 +48,12 @@ def zipfile(): ext = ext[1:] filename = generate_download_filename(song, ext) file = open(song.filename, 'rb') - yield "\x50\x4b\x03\x04" #local file header signature - yield "\x14\x00" #version needed to extract - yield "\x00\x00" #general purpose bit flag - yield "\x00\x00" #compression method - yield "\x00\x00" #last mod file time - yield "\x00\x00" #last mod file date + yield b"\x50\x4b\x03\x04" #local file header signature + yield b"\x14\x00" #version needed to extract + yield b"\x00\x00" #general purpose bit flag + yield b"\x00\x00" #compression method + yield b"\x00\x00" #last mod file time + yield b"\x00\x00" #last mod file date crc32 = 0 while True: data = file.read(8192) @@ -64,45 +64,46 @@ def zipfile(): crc32 = pack("<I", crc32 & 0xffffffff) yield crc32 #crc-32 size = os.path.getsize(song.filename) + filename_encoded = filename.encode('utf-8') yield pack("<I", size) #compressed size - yield pack("<I", size) #uncompressed size - yield pack("<H", len(filename)) #file name length - yield "\x00\x00" #extra field length - yield filename #file name + yield pack("<I", size) #uncompressed size + yield pack("<H", len(filename_encoded)) #file name length + yield b"\x00\x00" #extra field length + yield filename_encoded #file name while True: data = file.read(8192) if not data: break - yield data #file data - central_directory += "\x50\x4b\x01\x02" - central_directory += "\x00\x00" #version made by - central_directory += "\x14\x00" #version needed to extract - central_directory += "\x00\x00" #gen purpose bit flag - central_directory += "\x00\x00" #compression method - central_directory += "\x00\x00" #last mod file time - central_directory += "\x00\x00" #last mod file date - central_directory += crc32 #crc-32 - central_directory += pack("<I", size) #compressed filesize - central_directory += pack("<I", size) #uncompressed filesize - central_directory += pack("<H", len(filename)) #length of filename - central_directory += "\x00\x00" #extra field length - central_directory += "\x00\x00" #file comment length - central_directory += "\x00\x00" #disk number start - central_directory += "\x00\x00" #internal file attributes - central_directory += "\x20\x00\x00\x00" #external file attributes - 'archive' bit set (32) - central_directory += pack("<I", offset) #relative offset of local header - offset += 30 + len(filename) + size - central_directory += filename + yield data #file data + central_directory += b"\x50\x4b\x01\x02" + central_directory += b"\x00\x00" #version made by + central_directory += b"\x14\x00" #version needed to extract + central_directory += b"\x00\x00" #gen purpose bit flag + central_directory += b"\x00\x00" #compression method + central_directory += b"\x00\x00" #last mod file time + central_directory += b"\x00\x00" #last mod file date + central_directory += crc32 #crc-32 + central_directory += pack("<I", size) #compressed filesize + central_directory += pack("<I", size) #uncompressed filesize + central_directory += pack("<H", len(filename_encoded)) #length of filename + central_directory += b"\x00\x00" #extra field length + central_directory += b"\x00\x00" #file comment length + central_directory += b"\x00\x00" #disk number start + central_directory += b"\x00\x00" #internal file attributes + central_directory += b"\x20\x00\x00\x00" #external file attributes - 'archive' bit set (32) + central_directory += pack("<I", offset) #relative offset of local header + offset += 30 + len(filename_encoded) + size + central_directory += filename_encoded file.close() yield central_directory #central directory - yield "\x50\x4b\x05\x06" #end of central directory signature - yield "\x00\x00" #number of this disk - yield "\x00\x00" #number of the disk with the start of the central directory + yield b"\x50\x4b\x05\x06" #end of central directory signature + yield b"\x00\x00" #number of this disk + yield b"\x00\x00" #number of the disk with the start of the central directory yield pack("<H", count) #number of entries on disk yield pack("<H", count) #number of entries yield pack("<I", len(central_directory)) #size of central directory yield pack("<I", offset) #offset to start of central directory - yield "\x00\x00" #zip comment size - response = Response(do_zip(), mimetype="application/zip", direct_passthrough=True) + yield b"\x00\x00" #zip comment size + response = Response(do_zip(), mimetype="application/zip") response.headers.add('Content-Disposition', 'attachment', filename="ZX2C4Music-Download-" + str(int(time.time())) + ".zip") return response diff --git a/backend/zmusic/login.py b/backend/zmusic/login.py index 6e7b270..18e471a 100644 --- a/backend/zmusic/login.py +++ b/backend/zmusic/login.py @@ -26,7 +26,7 @@ def unauthorized(): def login_required(fn): @wraps(fn) def decorated_view(*args, **kwargs): - if query_is_admin_user(request.args) or query_is_music_user(request.args) or current_user.is_authenticated(): + if query_is_admin_user(request.args) or query_is_music_user(request.args) or current_user.is_authenticated: return fn(*args, **kwargs) return app.login_manager.unauthorized() return decorated_view @@ -40,7 +40,7 @@ def admin_required(fn): return decorated_view def is_admin(): - return query_is_admin_user(request.args) or (current_user.is_authenticated() and current_user.admin) + return query_is_admin_user(request.args) or (current_user.is_authenticated and current_user.admin) def query_is_music_user(query): username = query.get("username", None) diff --git a/backend/zmusic/picard/file.py b/backend/zmusic/picard/file.py index 0e246eb..beb0dac 100644 --- a/backend/zmusic/picard/file.py +++ b/backend/zmusic/picard/file.py @@ -41,8 +41,8 @@ class File(dict, object): self["length"] = file.info.length - hash = hashlib.new("sha1") - hash.update(str(metadata) + self["filename"]) + hash = hashlib.new("sha256") + hash.update((str(metadata) + self["filename"]).encode('utf-8')) self["id"] = hash.hexdigest() def __missing__(self, key): diff --git a/backend/zmusic/picard/formats/apev2.py b/backend/zmusic/picard/formats/apev2.py index 15c4808..2a0c62e 100644 --- a/backend/zmusic/picard/formats/apev2.py +++ b/backend/zmusic/picard/formats/apev2.py @@ -22,7 +22,7 @@ import mutagen.monkeysaudio import mutagen.musepack import mutagen.wavpack import mutagen.optimfrog -import mutagenext.tak +from zmusic.picard.formats.mutagenext import tak from zmusic.picard.file import File from zmusic.picard.metadata import Metadata from zmusic.picard.util import encode_filename, sanitize_date, mimetype @@ -46,7 +46,7 @@ class APEv2File(File): "MUSICBRAINZ_ALBUMSTATUS": "releasestatus", "MUSICBRAINZ_ALBUMTYPE": "releasetype", } - __rtranslate = dict([(v, k) for k, v in __translate.iteritems()]) + __rtranslate = dict([(v, k) for k, v in __translate.items()]) def _load(self, filename): self.log.debug("Loading file %r", filename) @@ -200,7 +200,7 @@ class TAKFile(APEv2File): """TAK file.""" EXTENSIONS = [".tak"] NAME = "Tom's lossless Audio Kompressor" - _File = mutagenext.tak.TAK + _File = tak.TAK def _info(self, metadata, file): super(TAKFile, self)._info(metadata, file) metadata['~format'] = self.NAME diff --git a/backend/zmusic/picard/formats/asf.py b/backend/zmusic/picard/formats/asf.py index 0be2c2a..ff2a3d6 100644 --- a/backend/zmusic/picard/formats/asf.py +++ b/backend/zmusic/picard/formats/asf.py @@ -136,9 +136,9 @@ class ASFFile(File): continue elif name == 'WM/SharedUserRating': # Rating in WMA ranges from 0 to 99, normalize this to the range 0 to 5 - values[0] = int(round(int(unicode(values[0])) / 99.0 * (5 - 1))) + values[0] = int(round(int(str(values[0])) / 99.0 * (5 - 1))) name = self.__RTRANS[name] - values = filter(bool, map(unicode, values)) + values = list(filter(bool, map(str, values))) if values: metadata[name] = values self._info(metadata, file) diff --git a/backend/zmusic/picard/formats/id3.py b/backend/zmusic/picard/formats/id3.py index 8e955d5..b7185b8 100644 --- a/backend/zmusic/picard/formats/id3.py +++ b/backend/zmusic/picard/formats/id3.py @@ -25,7 +25,7 @@ from zmusic.picard.metadata import Metadata from zmusic.picard.file import File from zmusic.picard.formats.mutagenext import compatid3 from zmusic.picard.util import encode_filename, sanitize_date -from urlparse import urlparse +from urllib.parse import urlparse # Ugly, but... I need to save the text in ISO-8859-1 even if it contains @@ -104,7 +104,7 @@ class ID3File(File): 'TPUB': 'label', 'TLAN': 'language', } - __rtranslate = dict([(v, k) for k, v in __translate.iteritems()]) + __rtranslate = dict([(v, k) for k, v in __translate.items()]) __translate_freetext = { 'MusicBrainz Artist Id': 'musicbrainz_artistid', @@ -127,7 +127,7 @@ class ID3File(File): 'ASIN': 'asin', 'MusicMagic Fingerprint': 'musicip_fingerprint', } - __rtranslate_freetext = dict([(v, k) for k, v in __translate_freetext.iteritems()]) + __rtranslate_freetext = dict([(v, k) for k, v in __translate_freetext.items()]) __tipl_roles = { 'engineer': 'engineer', @@ -136,7 +136,7 @@ class ID3File(File): 'DJ-mix': 'djmixer', 'mix': 'mixer', } - __rtipl_roles = dict([(v, k) for k, v in __tipl_roles.iteritems()]) + __rtipl_roles = dict([(v, k) for k, v in __tipl_roles.items()]) __other_supported_tags = ("discnumber", "tracknumber", "totaldiscs", "totaltracks") @@ -158,13 +158,13 @@ class ID3File(File): if frameid.startswith('T'): for text in frame.text: if text: - metadata.add(name, unicode(text)) + metadata.add(name, str(text)) elif frameid == 'COMM': for text in frame.text: if text: - metadata.add('%s:%s' % (name, frame.desc), unicode(text)) + metadata.add('%s:%s' % (name, frame.desc), str(text)) else: - metadata.add(name, unicode(frame)) + metadata.add(name, str(frame)) elif frameid == "TMCL": for role, name in frame.people: if role or name: @@ -179,14 +179,14 @@ class ID3File(File): else: name = str(frame.desc.lower()) for text in frame.text: - metadata.add(name, unicode(text)) + metadata.add(name, str(text)) elif frameid == 'USLT': name = 'lyrics' if frame.desc: name += ':%s' % frame.desc - metadata.add(name, unicode(frame.text)) + metadata.add(name, str(frame.text)) elif frameid == 'UFID' and frame.owner == 'http://musicbrainz.org': - metadata['musicbrainz_trackid'] = unicode(frame.data) + metadata['musicbrainz_trackid'] = str(frame.data) elif frameid == 'TRCK': value = frame.text[0].split('/') if len(value) > 1: @@ -204,7 +204,7 @@ class ID3File(File): elif frameid == 'POPM': # Rating in ID3 ranges from 0 to 255, normalize this to the range 0 to 5 if False: - rating = unicode(int(round(frame.rating / 255.0 * (5 - 1)))) + rating = str(int(round(frame.rating / 255.0 * (5 - 1)))) metadata.add('~rating', rating) if 'date' in metadata: diff --git a/backend/zmusic/picard/formats/mp4.py b/backend/zmusic/picard/formats/mp4.py index 2d1763f..edb1822 100644 --- a/backend/zmusic/picard/formats/mp4.py +++ b/backend/zmusic/picard/formats/mp4.py @@ -48,19 +48,19 @@ class MP4File(File): "tvsh": "show", "purl": "podcasturl", } - __r_text_tags = dict([(v, k) for k, v in __text_tags.iteritems()]) + __r_text_tags = dict([(v, k) for k, v in __text_tags.items()]) __bool_tags = { "pcst": "podcast", "cpil": "compilation", "pgap": "gapless", } - __r_bool_tags = dict([(v, k) for k, v in __bool_tags.iteritems()]) + __r_bool_tags = dict([(v, k) for k, v in __bool_tags.items()]) __int_tags = { "tmpo": "bpm", } - __r_int_tags = dict([(v, k) for k, v in __int_tags.iteritems()]) + __r_int_tags = dict([(v, k) for k, v in __int_tags.items()]) __freeform_tags = { "----:com.apple.iTunes:MusicBrainz Track Id": "musicbrainz_trackid", @@ -97,7 +97,7 @@ class MP4File(File): "----:com.apple.iTunes:SCRIPT": "script", "----:com.apple.iTunes:LANGUAGE": "language", } - __r_freeform_tags = dict([(v, k) for k, v in __freeform_tags.iteritems()]) + __r_freeform_tags = dict([(v, k) for k, v in __freeform_tags.items()]) __other_supported_tags = ("discnumber", "tracknumber", "totaldiscs", "totaltracks") @@ -117,14 +117,12 @@ class MP4File(File): metadata.add(self.__bool_tags[name], values and '1' or '0') elif name in self.__int_tags: for value in values: - metadata.add(self.__int_tags[name], unicode(value)) + metadata.add(self.__int_tags[name], str(value)) elif name in self.__freeform_tags: for value in values: - value = value.strip("\x00").decode("utf-8", "replace") metadata.add(self.__freeform_tags[name], value) elif name == "----:com.apple.iTunes:fingerprint": for value in values: - value = value.strip("\x00").decode("utf-8", "replace") if value.startswith("MusicMagic Fingerprint"): metadata.add("musicip_fingerprint", value[22:]) elif name == "trkn": @@ -135,9 +133,9 @@ class MP4File(File): metadata["totaldiscs"] = str(values[0][1]) elif name == "covr": for value in values: - if value.format == value.FORMAT_JPEG: + if value.imageformat == value.FORMAT_JPEG: metadata.add_image("image/jpeg", value) - elif value.format == value.FORMAT_PNG: + elif value.imageformat == value.FORMAT_PNG: metadata.add_image("image/png", value) self._info(metadata, file) @@ -165,7 +163,6 @@ class MP4File(File): except ValueError: pass elif name in self.__r_freeform_tags: - values = [v.encode("utf-8") for v in values] file.tags[self.__r_freeform_tags[name]] = values elif name == "musicip_fingerprint": file.tags["----:com.apple.iTunes:fingerprint"] = ["MusicMagic Fingerprint%s" % str(v) for v in values] diff --git a/backend/zmusic/picard/formats/mutagenext/compatid3.py b/backend/zmusic/picard/formats/mutagenext/compatid3.py index 8ca98a6..815106c 100644 --- a/backend/zmusic/picard/formats/mutagenext/compatid3.py +++ b/backend/zmusic/picard/formats/mutagenext/compatid3.py @@ -88,7 +88,7 @@ class CompatID3(ID3): if not framedata: try: self.delete(filename) - except EnvironmentError, err: + except EnvironmentError as err: from errno import ENOENT if err.errno != ENOENT: raise return @@ -98,7 +98,7 @@ class CompatID3(ID3): if filename is None: filename = self.filename try: f = open(filename, 'rb+') - except IOError, err: + except IOError as err: from errno import ENOENT if err.errno != ENOENT: raise f = open(filename, 'ab') # create, then reopen @@ -126,7 +126,7 @@ class CompatID3(ID3): try: f.seek(-128, 2) - except IOError, err: + except IOError as err: from errno import EINVAL if err.errno != EINVAL: raise f.seek(0, 2) # ensure read won't get "TAG" diff --git a/backend/zmusic/picard/formats/vorbis.py b/backend/zmusic/picard/formats/vorbis.py index f52bf06..3b1b8b0 100644 --- a/backend/zmusic/picard/formats/vorbis.py +++ b/backend/zmusic/picard/formats/vorbis.py @@ -61,11 +61,11 @@ class VCommentFile(File): elif name.startswith('rating'): try: name, email = name.split(':', 1) except ValueError: email = '' - continue + continue if email != self.config.setting['rating_user_email']: continue name = '~rating' - value = unicode(int(round((float(value) * (5 - 1))))) + value = str(int(round((float(value) * (5 - 1))))) elif name == "fingerprint" and value.startswith("MusicMagic Fingerprint"): name = "musicip_fingerprint" value = value[22:] @@ -115,7 +115,7 @@ class VCommentFile(File): name = 'rating:%s' % settings['rating_user_email'] else: name = 'rating' - value = unicode(float(value) / (settings['rating_steps'] - 1)) + value = str(float(value) / (settings['rating_steps'] - 1)) # don't save private tags elif name.startswith("~"): continue diff --git a/backend/zmusic/picard/metadata.py b/backend/zmusic/picard/metadata.py index 5074edf..4d556a2 100644 --- a/backend/zmusic/picard/metadata.py +++ b/backend/zmusic/picard/metadata.py @@ -70,7 +70,7 @@ class Metadata(object): values = self._items.get(name, None) if values: if len(values) > 1: - return MULTI_VALUED_JOINER.join(values) + return MULTI_VALUED_JOINER.join(map(str, values)) else: return values[0] else: @@ -109,8 +109,8 @@ class Metadata(object): def keys(self): return self._items.keys() - def iteritems(self): - for name, values in self._items.iteritems(): + def items(self): + for name, values in self._items.items(): for value in values: yield name, value @@ -120,7 +120,7 @@ class Metadata(object): >>> m.items() [("key1", "value1"), ("key1", "value2"), ("key2", "value3")] """ - return list(self.iteritems()) + return list(self.items()) def rawitems(self): """Returns the metadata items. diff --git a/backend/zmusic/picard/util/__init__.py b/backend/zmusic/picard/util/__init__.py index e0ad871..1b6d6cb 100644 --- a/backend/zmusic/picard/util/__init__.py +++ b/backend/zmusic/picard/util/__init__.py @@ -46,30 +46,17 @@ def asciipunct(s): u"—": u"-", u"―": u"--", } - for orig, repl in mapping.iteritems(): + for orig, repl in mapping.items(): s = s.replace(orig, repl) return s -_io_encoding = sys.getfilesystemencoding() - def encode_filename(filename): - """Encode unicode strings to filesystem encoding.""" - if isinstance(filename, unicode): - if os.path.supports_unicode_filenames and sys.platform != "darwin": - return filename - else: - return filename.encode(_io_encoding, 'replace') - else: - return filename + return filename def decode_filename(filename): - """Decode strings from filesystem encoding to unicode.""" - if isinstance(filename, unicode): - return filename - else: - return filename.decode(_io_encoding) + return filename def pathcmp(a, b): return os.path.normcase(a) == os.path.normcase(b) @@ -177,7 +164,7 @@ def make_short_filename(prefix, filename, max_path_length=240, max_length=200, break if left > 0: - raise IOError, "File name is too long." + raise IOError("File name is too long.") return os.path.join(*[a.strip() for a in reversed(parts)]) @@ -284,4 +271,4 @@ def load_release_type_scores(setting): def save_release_type_scores(scores): - return " ".join(["%s %.2f" % v for v in scores.iteritems()]) + return " ".join(["%s %.2f" % v for v in scores.items()]) diff --git a/backend/zmusic/streams.py b/backend/zmusic/streams.py index 3dca20d..c654155 100644 --- a/backend/zmusic/streams.py +++ b/backend/zmusic/streams.py @@ -20,7 +20,7 @@ class ProcessWrapper(object): return self def __del__(self): self.close() - def next(self): + def __next__(self): try: data = self.process.stdout.read(self.buffer_size) except: @@ -52,7 +52,7 @@ class PartialFileWrapper(object): self.file.close() def __iter__(self): return self - def next(self): + def __next__(self): if self.length is not None: if self.length <= 0: raise StopIteration() @@ -1,13 +1,10 @@ LOCAL_COLLECTION_PATH = /home/zx2c4/Music -UPLOAD_SERVER = music.zx2c4.com -UPLOAD_SERVER_PATH = /mnt/storage/music -WEB_SERVER = music.zx2c4.com +WEB_SERVER = root@music.zx2c4.com SERVER_STATIC_PATH = /var/www/htdocs/music.zx2c4.com SERVER_APP_PATH = /var/www/uwsgi/zmusic SERVER_MUSIC_PATH = /mnt/storage/music -SERVER_APP_USER = zmusic -SERVER_STATIC_USER = nginx +URL_BASE = https://music.zx2c4.com ADMIN_USERNAME = msadmin ADMIN_PASSWORD = 2manyservers |