aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile36
-rw-r--r--README.md7
-rw-r--r--backend/zmusic/database.py37
-rw-r--r--backend/zmusic/endpoints/error.py4
-rw-r--r--backend/zmusic/endpoints/login.py2
-rw-r--r--backend/zmusic/endpoints/scan.py12
-rw-r--r--backend/zmusic/endpoints/zip.py73
-rw-r--r--backend/zmusic/login.py4
-rw-r--r--backend/zmusic/picard/file.py4
-rw-r--r--backend/zmusic/picard/formats/apev2.py6
-rw-r--r--backend/zmusic/picard/formats/asf.py4
-rw-r--r--backend/zmusic/picard/formats/id3.py22
-rw-r--r--backend/zmusic/picard/formats/mp4.py17
-rw-r--r--backend/zmusic/picard/formats/mutagenext/compatid3.py6
-rw-r--r--backend/zmusic/picard/formats/vorbis.py6
-rw-r--r--backend/zmusic/picard/metadata.py8
-rw-r--r--backend/zmusic/picard/util/__init__.py23
-rw-r--r--backend/zmusic/streams.py4
-rw-r--r--server.cfg7
19 files changed, 121 insertions, 161 deletions
diff --git a/Makefile b/Makefile
index 8902d6f..9b4300c 100644
--- a/Makefile
+++ b/Makefile
@@ -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)'
diff --git a/README.md b/README.md
index 6917579..58f9e81 100644
--- a/README.md
+++ b/README.md
@@ -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()
diff --git a/server.cfg b/server.cfg
index 52038ef..873b785 100644
--- a/server.cfg
+++ b/server.cfg
@@ -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