mirror of
https://github.com/fergalmoran/picard.git
synced 2026-02-21 23:24:17 +00:00
Merge pull request #1123 from zas/const.sys
Introduce picard.const.sys: IS_WIN, IS_MACOS, IS_LINUX, IS_FROZEN
This commit is contained in:
@@ -28,9 +28,9 @@ from picard import (
|
||||
)
|
||||
from picard.acoustid.json_helpers import parse_recording
|
||||
from picard.const import FPCALC_NAMES
|
||||
from picard.const.sys import IS_FROZEN
|
||||
from picard.util import (
|
||||
find_executable,
|
||||
is_frozen,
|
||||
)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class AcoustIDClient(QtCore.QObject):
|
||||
# The second condition is checked because in case of a packaged build of picard
|
||||
# the temp directory that pyinstaller decompresses picard into changes on every
|
||||
# launch, thus we need to ignore the existing config values.
|
||||
if not config.setting["acoustid_fpcalc"] or is_frozen:
|
||||
if not config.setting["acoustid_fpcalc"] or IS_FROZEN:
|
||||
fpcalc_path = find_executable(*FPCALC_NAMES)
|
||||
if fpcalc_path:
|
||||
config.setting["acoustid_fpcalc"] = fpcalc_path
|
||||
|
||||
@@ -26,12 +26,12 @@ from heapq import (
|
||||
import ntpath
|
||||
from operator import itemgetter
|
||||
import re
|
||||
import sys
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from picard import config
|
||||
from picard.const import QUERY_LIMIT
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.metadata import Metadata
|
||||
from picard.similarity import similarity
|
||||
from picard.util import (
|
||||
@@ -248,7 +248,7 @@ class Cluster(QtCore.QObject, Item):
|
||||
|
||||
@staticmethod
|
||||
def cluster(files, threshold):
|
||||
win_compat = config.setting["windows_compatibility"] or sys.platform == "win32"
|
||||
win_compat = config.setting["windows_compatibility"] or IS_WIN
|
||||
artist_dict = ClusterDict()
|
||||
album_dict = ClusterDict()
|
||||
tracks = []
|
||||
|
||||
29
picard/const/sys.py
Normal file
29
picard/const/sys.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
# Copyright (C) 2019 Laurent Monin
|
||||
#
|
||||
# This program 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 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
|
||||
IS_WIN = sys.platform == 'win32'
|
||||
IS_LINUX = sys.platform == 'linux'
|
||||
IS_MACOS = sys.platform == 'darwin'
|
||||
|
||||
# These variables are set by pyinstaller if running from a packaged build
|
||||
# See http://pyinstaller.readthedocs.io/en/stable/runtime-information.html
|
||||
IS_FROZEN = getattr(sys, 'frozen', False)
|
||||
FROZEN_TEMP_PATH = getattr(sys, '_MEIPASS', '')
|
||||
@@ -26,7 +26,6 @@ import os
|
||||
import os.path
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
from PyQt5 import QtCore
|
||||
@@ -37,6 +36,10 @@ from picard import (
|
||||
log,
|
||||
)
|
||||
from picard.const import QUERY_LIMIT
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
from picard.metadata import Metadata
|
||||
from picard.util import (
|
||||
decode_filename,
|
||||
@@ -351,14 +354,14 @@ class File(QtCore.QObject, Item):
|
||||
# TODO: move following logic under util.filenaming
|
||||
# (and reconsider its necessity)
|
||||
# win32 compatibility fixes
|
||||
if settings['windows_compatibility'] or sys.platform == 'win32':
|
||||
if settings['windows_compatibility'] or IS_WIN:
|
||||
new_filename = new_filename.replace('./', '_/').replace('.\\', '_\\')
|
||||
# replace . at the beginning of file and directory names
|
||||
new_filename = new_filename.replace('/.', '/_').replace('\\.', '\\_')
|
||||
if new_filename and new_filename[0] == '.':
|
||||
new_filename = '_' + new_filename[1:]
|
||||
# Fix for precomposed characters on OSX
|
||||
if sys.platform == "darwin":
|
||||
if IS_MACOS:
|
||||
new_filename = unicodedata.normalize("NFD", new_filename)
|
||||
|
||||
new_path = os.path.join(new_dirname, new_filename)
|
||||
|
||||
@@ -21,10 +21,14 @@ import builtins
|
||||
import gettext
|
||||
import locale
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
from PyQt5.QtCore import QLocale
|
||||
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
|
||||
builtins.__dict__['N_'] = lambda a: a
|
||||
|
||||
|
||||
@@ -40,7 +44,7 @@ def setup_gettext(localedir, ui_language=None, logger=None):
|
||||
except Exception as e:
|
||||
logger(e)
|
||||
else:
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
from ctypes import windll
|
||||
try:
|
||||
current_locale = locale.windows_locale[windll.kernel32.GetUserDefaultUILanguage()]
|
||||
@@ -53,7 +57,7 @@ def setup_gettext(localedir, ui_language=None, logger=None):
|
||||
logger(e)
|
||||
except Exception as e:
|
||||
logger(e)
|
||||
elif sys.platform == 'darwin':
|
||||
elif IS_MACOS:
|
||||
try:
|
||||
import Foundation
|
||||
defaults = Foundation.NSUserDefaults.standardUserDefaults()
|
||||
|
||||
@@ -58,6 +58,11 @@ from picard.cluster import (
|
||||
)
|
||||
from picard.collection import load_user_collections
|
||||
from picard.config_upgrade import upgrade_config
|
||||
from picard.const.sys import (
|
||||
IS_FROZEN,
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
from picard.const import (
|
||||
USER_DIR,
|
||||
USER_PLUGIN_DIR,
|
||||
@@ -130,7 +135,7 @@ class Tagger(QtWidgets.QApplication):
|
||||
|
||||
# Use the new fusion style from PyQt5 for a modern and consistent look
|
||||
# across all OSes.
|
||||
if sys.platform != "darwin":
|
||||
if not IS_MACOS:
|
||||
self.setStyle('Fusion')
|
||||
|
||||
# Set the WM_CLASS to 'MusicBrainz-Picard' so desktop environments
|
||||
@@ -171,7 +176,7 @@ class Tagger(QtWidgets.QApplication):
|
||||
self.save_thread_pool = QtCore.QThreadPool(self)
|
||||
self.save_thread_pool.setMaxThreadCount(1)
|
||||
|
||||
if not sys.platform == "win32":
|
||||
if not IS_WIN:
|
||||
# Set up signal handling
|
||||
# It's not possible to call all available functions from signal
|
||||
# handlers, therefore we need to set up a QSocketNotifier to listen
|
||||
@@ -194,7 +199,7 @@ class Tagger(QtWidgets.QApplication):
|
||||
signal.signal(signal.SIGINT, self.signal)
|
||||
signal.signal(signal.SIGTERM, self.signal)
|
||||
|
||||
if sys.platform == "darwin":
|
||||
if IS_MACOS:
|
||||
# On macOS it is not common that the global menu shows icons
|
||||
self.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus)
|
||||
|
||||
@@ -233,7 +238,7 @@ class Tagger(QtWidgets.QApplication):
|
||||
# Load plugins
|
||||
self.pluginmanager = PluginManager()
|
||||
if not self._no_plugins:
|
||||
if hasattr(sys, "frozen"):
|
||||
if IS_FROZEN:
|
||||
self.pluginmanager.load_plugindir(os.path.join(os.path.dirname(sys.argv[0]), "plugins"))
|
||||
else:
|
||||
mydir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt5 import (
|
||||
QtCore,
|
||||
@@ -28,6 +27,7 @@ from PyQt5 import (
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
|
||||
from picard import config
|
||||
from picard.const.sys import IS_MACOS
|
||||
from picard.formats import supported_formats
|
||||
from picard.util import find_existing_path
|
||||
|
||||
@@ -73,7 +73,7 @@ class FileBrowser(QtWidgets.QTreeView):
|
||||
self.model.setNameFilterDisables(False)
|
||||
self.model.sort(0, QtCore.Qt.AscendingOrder)
|
||||
self.setModel(self.model)
|
||||
if sys.platform == "darwin":
|
||||
if IS_MACOS:
|
||||
self.setRootIndex(self.model.index("/Volumes"))
|
||||
header = self.header()
|
||||
header.hideSection(1)
|
||||
|
||||
@@ -21,7 +21,6 @@ from collections import OrderedDict
|
||||
import datetime
|
||||
from functools import partial
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
from PyQt5 import (
|
||||
QtCore,
|
||||
@@ -36,6 +35,9 @@ from picard import (
|
||||
from picard.album import Album
|
||||
from picard.cluster import Cluster
|
||||
from picard.const import PROGRAM_UPDATE_LEVELS
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
)
|
||||
from picard.file import File
|
||||
from picard.formats import supported_formats
|
||||
from picard.plugin import ExtensionPoint
|
||||
@@ -122,14 +124,14 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
icon.addFile(":/images/256x256/org.musicbrainz.Picard.png", QtCore.QSize(256, 256))
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
self.show_close_window = sys.platform == "darwin"
|
||||
self.show_close_window = IS_MACOS
|
||||
|
||||
self.create_actions()
|
||||
self.create_statusbar()
|
||||
self.create_toolbar()
|
||||
self.create_menus()
|
||||
|
||||
if sys.platform == "darwin":
|
||||
if IS_MACOS:
|
||||
self.setUnifiedTitleAndToolBarOnMac(True)
|
||||
self.toolbar.setMovable(False)
|
||||
self.search_toolbar.setMovable(False)
|
||||
@@ -174,7 +176,7 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
def keyPressEvent(self, event):
|
||||
# On macOS Command+Backspace triggers the so called "Forward Delete".
|
||||
# It should be treated the same as the Delete button.
|
||||
is_forward_delete = sys.platform == 'darwin' and \
|
||||
is_forward_delete = IS_MACOS and \
|
||||
event.key() == QtCore.Qt.Key_Backspace and \
|
||||
event.modifiers() & QtCore.Qt.ControlModifier
|
||||
if event.matches(QtGui.QKeySequence.Delete) or is_forward_delete:
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
from functools import partial
|
||||
from operator import attrgetter
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
from PyQt5 import (
|
||||
QtCore,
|
||||
@@ -36,10 +35,12 @@ from picard import (
|
||||
config,
|
||||
log,
|
||||
)
|
||||
|
||||
from picard.const import (
|
||||
PLUGINS_API,
|
||||
USER_PLUGIN_DIR,
|
||||
)
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.util import reconnect
|
||||
|
||||
from picard.ui import HashableTreeWidgetItem
|
||||
@@ -633,7 +634,7 @@ class PluginsOptionsPage(OptionsPage):
|
||||
|
||||
@staticmethod
|
||||
def open_plugin_dir():
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
url = 'file:///' + USER_PLUGIN_DIR
|
||||
else:
|
||||
url = 'file://' + USER_PLUGIN_DIR
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
from locale import strxfrm
|
||||
from operator import itemgetter
|
||||
import sys
|
||||
|
||||
from PyQt5 import (
|
||||
QtCore,
|
||||
@@ -27,12 +26,14 @@ from PyQt5 import (
|
||||
)
|
||||
|
||||
from picard import config
|
||||
|
||||
from picard.const import (
|
||||
RELEASE_COUNTRIES,
|
||||
RELEASE_FORMATS,
|
||||
RELEASE_PRIMARY_GROUPS,
|
||||
RELEASE_SECONDARY_GROUPS,
|
||||
)
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.i18n import gettext_attr
|
||||
|
||||
from picard.ui.options import (
|
||||
@@ -67,12 +68,12 @@ class TipSlider(QtWidgets.QSlider):
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
if sys.platform != 'win32':
|
||||
if not IS_WIN:
|
||||
self.valueChanged.connect(self.show_tip)
|
||||
|
||||
def hideEvent(self, event):
|
||||
super().hideEvent(event)
|
||||
if sys.platform != 'win32':
|
||||
if not IS_WIN:
|
||||
self.valueChanged.disconnect(self.show_tip)
|
||||
|
||||
def show_tip(self, value):
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
from functools import partial
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
@@ -28,6 +27,7 @@ from PyQt5.QtGui import QPalette
|
||||
|
||||
from picard import config
|
||||
from picard.const import PICARD_URLS
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.file import File
|
||||
from picard.script import (
|
||||
ScriptError,
|
||||
@@ -125,7 +125,7 @@ class RenamingOptionsPage(OptionsPage):
|
||||
self.ui.file_naming_format_default.setEnabled(state)
|
||||
self.ui.ascii_filenames.setEnabled(state)
|
||||
self.ui.file_naming_format_group.setEnabled(state)
|
||||
if not sys.platform == "win32":
|
||||
if not IS_WIN:
|
||||
self.ui.windows_compatibility.setEnabled(state)
|
||||
|
||||
if self.ui.file_naming_format.isEnabled():
|
||||
@@ -171,7 +171,7 @@ class RenamingOptionsPage(OptionsPage):
|
||||
self.ui.example_filename_va.setText(example2)
|
||||
|
||||
def load(self):
|
||||
if sys.platform == "win32":
|
||||
if IS_WIN:
|
||||
self.ui.windows_compatibility.setChecked(True)
|
||||
self.ui.windows_compatibility.setEnabled(False)
|
||||
else:
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
|
||||
from PyQt5 import (
|
||||
QtCore,
|
||||
QtGui,
|
||||
@@ -26,6 +24,10 @@ from PyQt5 import (
|
||||
)
|
||||
|
||||
from picard import config
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
from picard.util import (
|
||||
find_existing_path,
|
||||
icontheme,
|
||||
@@ -49,7 +51,7 @@ class StandardButton(QtWidgets.QPushButton):
|
||||
def __init__(self, btntype):
|
||||
label = _(self.__types[btntype][0])
|
||||
args = [label]
|
||||
if sys.platform != 'win32' and sys.platform != 'darwin':
|
||||
if not IS_WIN and not IS_MACOS:
|
||||
iconname = self.__types[btntype][1]
|
||||
if hasattr(QtWidgets.QStyle, iconname):
|
||||
icon = self.tagger.style().standardIcon(getattr(QtWidgets.QStyle, iconname))
|
||||
@@ -87,7 +89,7 @@ class MultiDirsSelectDialog(QtWidgets.QFileDialog):
|
||||
super().__init__(*args)
|
||||
self.setFileMode(self.Directory)
|
||||
self.setOption(self.ShowDirsOnly)
|
||||
if sys.platform in ["darwin", "win32"]:
|
||||
if IS_WIN or IS_MACOS:
|
||||
# The native dialog doesn't allow selecting >1 directory
|
||||
self.setOption(self.DontUseNativeDialog)
|
||||
for view in self.findChildren((QtWidgets.QListView, QtWidgets.QTreeView)):
|
||||
|
||||
@@ -31,17 +31,18 @@ from PyQt5 import QtCore
|
||||
|
||||
# Required for compatibility with lastfmplus which imports this from here rather than loading it direct.
|
||||
from picard.const import MUSICBRAINZ_SERVERS
|
||||
from picard.const.sys import (
|
||||
FROZEN_TEMP_PATH,
|
||||
IS_FROZEN,
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
|
||||
if IS_WIN:
|
||||
from ctypes import windll
|
||||
|
||||
|
||||
# These variables are set by pyinstaller if running from a packaged build
|
||||
# See http://pyinstaller.readthedocs.io/en/stable/runtime-information.html
|
||||
is_frozen = getattr(sys, 'frozen', False)
|
||||
frozen_temp_path = getattr(sys, '_MEIPASS', '')
|
||||
|
||||
|
||||
class LockableObject(QtCore.QObject):
|
||||
|
||||
"""Read/write lockable object."""
|
||||
@@ -142,7 +143,7 @@ def replace_win32_incompat(string, repl="_"):
|
||||
"""Replace win32 filename incompatible characters from ``string`` by
|
||||
``repl``."""
|
||||
# Don't replace : with _ for windows drive
|
||||
if sys.platform == "win32" and os.path.isabs(string):
|
||||
if IS_WIN and os.path.isabs(string):
|
||||
drive, rest = ntpath.splitdrive(string)
|
||||
return drive + _re_win32_incompat.sub(repl, rest)
|
||||
else:
|
||||
@@ -200,13 +201,13 @@ def find_existing_path(path):
|
||||
|
||||
|
||||
def find_executable(*executables):
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
executables = [e + '.exe' for e in executables]
|
||||
paths = [os.path.dirname(sys.executable)] if sys.executable else []
|
||||
paths += os.environ.get('PATH', '').split(os.pathsep)
|
||||
# This is for searching for executables bundled in packaged builds
|
||||
if is_frozen:
|
||||
paths += [frozen_temp_path]
|
||||
if IS_FROZEN:
|
||||
paths += [FROZEN_TEMP_PATH]
|
||||
for path in paths:
|
||||
for executable in executables:
|
||||
f = os.path.join(path, executable)
|
||||
@@ -317,7 +318,7 @@ def tracknum_from_filename(base_filename):
|
||||
|
||||
|
||||
# Provide os.path.samefile equivalent which is missing in Python under Windows
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
def os_path_samefile(p1, p2):
|
||||
ap1 = os.path.abspath(p1)
|
||||
ap2 = os.path.abspath(p2)
|
||||
@@ -332,12 +333,12 @@ def is_hidden(filepath):
|
||||
on non-Windows systems or if it has the "hidden" flag
|
||||
set on Windows."""
|
||||
name = os.path.basename(os.path.abspath(filepath))
|
||||
return (name.startswith('.') and sys.platform != 'win32') \
|
||||
return (not IS_WIN and name.startswith('.')) \
|
||||
or _has_hidden_attribute(filepath)
|
||||
|
||||
|
||||
def _has_hidden_attribute(filepath):
|
||||
if sys.platform != 'win32':
|
||||
if not IS_WIN:
|
||||
return False
|
||||
# FIXME: On OSX detecting hidden files involves more
|
||||
# than just checking for dot files, see
|
||||
|
||||
@@ -18,17 +18,19 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QFile,
|
||||
QIODevice,
|
||||
)
|
||||
|
||||
from picard import config
|
||||
from picard.const.sys import (
|
||||
IS_LINUX,
|
||||
IS_WIN,
|
||||
)
|
||||
from picard.util import uniqify
|
||||
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
from ctypes import windll
|
||||
|
||||
|
||||
@@ -50,9 +52,9 @@ if discid is not None:
|
||||
LINUX_CDROM_INFO = '/proc/sys/dev/cdrom/info'
|
||||
|
||||
# if get_cdrom_drives() lists ALL drives available on the machine
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
AUTO_DETECT_DRIVES = True
|
||||
elif sys.platform == 'linux' and QFile.exists(LINUX_CDROM_INFO):
|
||||
elif IS_LINUX and QFile.exists(LINUX_CDROM_INFO):
|
||||
AUTO_DETECT_DRIVES = True
|
||||
else:
|
||||
# There might be more drives we couldn't detect
|
||||
@@ -66,7 +68,7 @@ def get_cdrom_drives():
|
||||
# add default drive from libdiscid to the list
|
||||
drives = list(DEFAULT_DRIVES)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
if IS_WIN:
|
||||
GetLogicalDrives = windll.kernel32.GetLogicalDrives
|
||||
GetDriveType = windll.kernel32.GetDriveTypeW
|
||||
DRIVE_CDROM = 5
|
||||
@@ -77,7 +79,7 @@ def get_cdrom_drives():
|
||||
if GetDriveType(drive) == DRIVE_CDROM:
|
||||
drives.append(drive)
|
||||
|
||||
elif sys.platform == 'linux' and QFile.exists(LINUX_CDROM_INFO):
|
||||
elif IS_LINUX and QFile.exists(LINUX_CDROM_INFO):
|
||||
# Read info from /proc/sys/dev/cdrom/info
|
||||
cdinfo = QFile(LINUX_CDROM_INFO)
|
||||
if cdinfo.open(QIODevice.ReadOnly | QIODevice.Text):
|
||||
|
||||
@@ -25,6 +25,10 @@ import unicodedata
|
||||
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
from picard.util import (
|
||||
_io_encoding,
|
||||
decode_filename,
|
||||
@@ -324,7 +328,7 @@ def make_short_filename(basedir, relpath, win_compat=False, relative_to=""):
|
||||
# always strip the relpath parts
|
||||
relpath = os.path.join(*[part.strip() for part in relpath.split(os.path.sep)])
|
||||
# if we're on windows, delegate the work to a windows-specific function
|
||||
if sys.platform == "win32":
|
||||
if IS_WIN:
|
||||
reserved = len(basedir)
|
||||
if not basedir.endswith(os.path.sep):
|
||||
reserved += 1
|
||||
@@ -345,7 +349,7 @@ def make_short_filename(basedir, relpath, win_compat=False, relative_to=""):
|
||||
relpath = _make_win_short_filename(relpath, reserved)
|
||||
# on *nix we can consider there is no path limit, but there is
|
||||
# a filename length limit.
|
||||
if sys.platform == "darwin":
|
||||
if IS_MACOS:
|
||||
# on OS X (i.e. HFS+), this is expressed in UTF-16 code points,
|
||||
# in NFD normalization form
|
||||
relpath = shorten_path(relpath, 255, mode=SHORTEN_UTF16_NFD)
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
from PyQt5 import QtGui
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from picard.const.sys import IS_WIN
|
||||
|
||||
|
||||
if IS_WIN:
|
||||
_search_paths = []
|
||||
else:
|
||||
_search_paths = [
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
|
||||
from picard import config
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.script import ScriptParser
|
||||
from picard.util import (
|
||||
replace_win32_incompat,
|
||||
@@ -42,7 +41,7 @@ def script_to_filename(naming_format, metadata, file=None, settings=None):
|
||||
if settings["ascii_filenames"]:
|
||||
filename = replace_non_ascii(filename, pathsave=True)
|
||||
# replace incompatible characters
|
||||
if settings["windows_compatibility"] or sys.platform == "win32":
|
||||
if settings["windows_compatibility"] or IS_WIN:
|
||||
filename = replace_win32_incompat(filename)
|
||||
# remove null characters
|
||||
filename = filename.replace("\x00", "")
|
||||
|
||||
12
setup.py
12
setup.py
@@ -11,7 +11,6 @@ import glob
|
||||
from io import StringIO
|
||||
import os
|
||||
from os import path
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -27,6 +26,11 @@ from picard import (
|
||||
PICARD_VERSION,
|
||||
__version__,
|
||||
)
|
||||
from picard.const.sys import (
|
||||
IS_LINUX,
|
||||
IS_WIN,
|
||||
)
|
||||
|
||||
|
||||
if sys.version_info < (3, 5):
|
||||
sys.exit("ERROR: You need Python 3.5 or higher to use Picard.")
|
||||
@@ -213,7 +217,7 @@ class picard_build(build):
|
||||
def run(self):
|
||||
log.info('generating scripts/%s from scripts/picard.in', PACKAGE_NAME)
|
||||
generate_file('scripts/picard.in', 'scripts/' + PACKAGE_NAME, {'localedir': self.localedir, 'autoupdate': not self.disable_autoupdate})
|
||||
if platform.system() == 'Windows':
|
||||
if IS_WIN:
|
||||
# Temporarily setting it to this value to generate a nice name for Windows app
|
||||
args['name'] = 'MusicBrainz Picard'
|
||||
file_version = PICARD_VERSION[0:3] + PICARD_VERSION[4:]
|
||||
@@ -225,7 +229,7 @@ class picard_build(build):
|
||||
}
|
||||
generate_file('win-version-info.txt.in', 'win-version-info.txt', {**args, **version_args})
|
||||
args['name'] = 'picard'
|
||||
elif platform.system() == 'Linux':
|
||||
elif IS_LINUX:
|
||||
self.run_command('build_appdata')
|
||||
build.run(self)
|
||||
|
||||
@@ -734,7 +738,7 @@ args['data_files'] = [
|
||||
('share/applications', ('org.musicbrainz.Picard.desktop',)),
|
||||
]
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
if IS_LINUX:
|
||||
args['data_files'].append(('share/metainfo', ['org.musicbrainz.Picard.appdata.xml']))
|
||||
|
||||
setup(**args)
|
||||
|
||||
15
tagger.py
15
tagger.py
@@ -4,11 +4,16 @@ import os.path
|
||||
import sys
|
||||
|
||||
|
||||
from picard.const.sys import (
|
||||
FROZEN_TEMP_PATH,
|
||||
IS_FROZEN,
|
||||
IS_WIN,
|
||||
)
|
||||
# On Windows try to attach to the console as early as possible in order
|
||||
# to get stdout / stderr logged to console. This needs to happen before
|
||||
# logging gets imported.
|
||||
# See https://stackoverflow.com/questions/54536/win32-gui-app-that-writes-usage-text-to-stdout-when-invoked-as-app-exe-help
|
||||
if sys.platform == "win32":
|
||||
if IS_WIN:
|
||||
from ctypes import windll
|
||||
if windll.kernel32.AttachConsole(-1):
|
||||
sys.stdout = open('CON', 'w')
|
||||
@@ -16,16 +21,12 @@ if sys.platform == "win32":
|
||||
|
||||
|
||||
from picard.tagger import main
|
||||
from picard.util import (
|
||||
frozen_temp_path,
|
||||
is_frozen,
|
||||
)
|
||||
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
# This is needed to find resources when using pyinstaller
|
||||
if is_frozen:
|
||||
basedir = frozen_temp_path
|
||||
if IS_FROZEN:
|
||||
basedir = FROZEN_TEMP_PATH
|
||||
else:
|
||||
basedir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import sys
|
||||
|
||||
from test.picardtestcase import PicardTestCase
|
||||
|
||||
from picard import config
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.file import File
|
||||
from picard.metadata import Metadata
|
||||
from picard.script import register_script_function
|
||||
@@ -68,7 +67,7 @@ class ScriptToFilenameTest(PicardTestCase):
|
||||
expect_orig = '*:?'
|
||||
expect_compat = '___'
|
||||
filename = script_to_filename('%artist%?', metadata, settings=settings)
|
||||
self.assertEqual(expect_compat if sys.platform == 'win32' else expect_orig, filename)
|
||||
self.assertEqual(expect_compat if IS_WIN else expect_orig, filename)
|
||||
settings['windows_compatibility'] = True
|
||||
filename = script_to_filename('%artist%?', metadata, settings=settings)
|
||||
self.assertEqual(expect_compat, filename)
|
||||
|
||||
@@ -6,28 +6,30 @@ import sys
|
||||
from test.picardtestcase import PicardTestCase
|
||||
import unittest
|
||||
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
)
|
||||
from picard.util.filenaming import make_short_filename
|
||||
|
||||
|
||||
class ShortFilenameTest(PicardTestCase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.maxDiff = None
|
||||
is_win32 = sys.platform == "win32"
|
||||
self.root = os.path.join(is_win32 and "X:\\" or "/", "x" * 10)
|
||||
if is_win32:
|
||||
self.root = os.path.join(IS_WIN and "X:\\" or "/", "x" * 10)
|
||||
if IS_WIN:
|
||||
self.max_len = 255
|
||||
else:
|
||||
self.max_len = os.statvfs("/").f_namemax
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@unittest.skipUnless(sys.platform in ("win32", "darwin"), "windows / os x test")
|
||||
@unittest.skipUnless(IS_WIN or IS_MACOS, "windows / os x test")
|
||||
def test_bmp_unicode_on_unicode_fs(self):
|
||||
char = u"\N{LATIN SMALL LETTER SHARP S}"
|
||||
fn = make_short_filename(self.root, os.path.join(*[char * 120] * 2))
|
||||
self.assertEqual(fn, os.path.join(*[char * 120] * 2))
|
||||
|
||||
@unittest.skipUnless(sys.platform not in ("win32", "darwin"), "non-windows, non-osx test")
|
||||
@unittest.skipUnless(not IS_WIN and not IS_MACOS, "non-windows, non-osx test")
|
||||
def test_bmp_unicode_on_nix(self):
|
||||
char = u"\N{LATIN SMALL LETTER SHARP S}"
|
||||
max_len = self.max_len
|
||||
@@ -35,28 +37,28 @@ class ShortFilenameTest(PicardTestCase):
|
||||
fn = make_short_filename(self.root, os.path.join(*[char * 200] * 2))
|
||||
self.assertEqual(fn, os.path.join(*[char * (max_len // divisor)] * 2))
|
||||
|
||||
@unittest.skipUnless(sys.platform == "darwin", "os x test")
|
||||
@unittest.skipUnless(IS_MACOS, "os x test")
|
||||
def test_precomposed_unicode_on_osx(self):
|
||||
char = u"\N{LATIN SMALL LETTER A WITH BREVE}"
|
||||
max_len = self.max_len
|
||||
fn = make_short_filename(self.root, os.path.join(*[char * 200] * 2))
|
||||
self.assertEqual(fn, os.path.join(*[char * (max_len // 2)] * 2))
|
||||
|
||||
@unittest.skipUnless(sys.platform == "win32", "windows test")
|
||||
@unittest.skipUnless(IS_WIN, "windows test")
|
||||
def test_nonbmp_unicode_on_windows(self):
|
||||
char = u"\N{MUSICAL SYMBOL G CLEF}"
|
||||
remaining = 259 - (3 + 10 + 1 + 200 + 1)
|
||||
fn = make_short_filename(self.root, os.path.join(*[char * 100] * 2))
|
||||
self.assertEqual(fn, os.path.join(char * 100, char * (remaining // 2)))
|
||||
|
||||
@unittest.skipUnless(sys.platform == "darwin", "os x test")
|
||||
@unittest.skipUnless(IS_MACOS, "os x test")
|
||||
def test_nonbmp_unicode_on_osx(self):
|
||||
char = u"\N{MUSICAL SYMBOL G CLEF}"
|
||||
max_len = self.max_len
|
||||
fn = make_short_filename(self.root, os.path.join(*[char * 200] * 2))
|
||||
self.assertEqual(fn, os.path.join(*[char * (max_len // 2)] * 2))
|
||||
|
||||
@unittest.skipUnless(sys.platform not in ("win32", "darwin"), "non-windows, non-osx test")
|
||||
@unittest.skipUnless(not IS_WIN and not IS_MACOS, "non-windows, non-osx test")
|
||||
def test_nonbmp_unicode_on_nix(self):
|
||||
char = u"\N{MUSICAL SYMBOL G CLEF}"
|
||||
max_len = self.max_len
|
||||
@@ -64,7 +66,7 @@ class ShortFilenameTest(PicardTestCase):
|
||||
fn = make_short_filename(self.root, os.path.join(*[char * 100] * 2))
|
||||
self.assertEqual(fn, os.path.join(*[char * (max_len // divisor)] * 2))
|
||||
|
||||
@unittest.skipUnless(sys.platform not in ("win32", "darwin"), "non-windows, non-osx test")
|
||||
@unittest.skipUnless(not IS_WIN and not IS_MACOS, "non-windows, non-osx test")
|
||||
def test_nonbmp_unicode_on_nix_with_windows_compat(self):
|
||||
char = u"\N{MUSICAL SYMBOL G CLEF}"
|
||||
max_len = self.max_len
|
||||
@@ -77,7 +79,7 @@ class ShortFilenameTest(PicardTestCase):
|
||||
fn = make_short_filename(self.root, os.path.join("a" * 200, "b" * 200, "c" * 200 + ".ext"), win_compat=True)
|
||||
self.assertEqual(fn, os.path.join("a" * 116, "b" * 116, "c" * 7 + ".ext"))
|
||||
|
||||
@unittest.skipUnless(sys.platform != "win32", "non-windows test")
|
||||
@unittest.skipUnless(not IS_WIN, "non-windows test")
|
||||
def test_windows_shortening_with_ancestor_on_nix(self):
|
||||
root = os.path.join(self.root, "w" * 10, "x" * 10, "y" * 9, "z" * 9)
|
||||
fn = make_short_filename(
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import builtins
|
||||
import os.path
|
||||
import sys
|
||||
from test.picardtestcase import PicardTestCase
|
||||
import unittest
|
||||
|
||||
from picard import util
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.util import imageinfo
|
||||
|
||||
# ensure _() is defined
|
||||
@@ -16,14 +16,14 @@ if '_' not in builtins.__dict__:
|
||||
|
||||
class ReplaceWin32IncompatTest(PicardTestCase):
|
||||
|
||||
@unittest.skipUnless(sys.platform == "win32", "windows test")
|
||||
@unittest.skipUnless(IS_WIN, "windows test")
|
||||
def test_correct_absolute_win32(self):
|
||||
self.assertEqual(util.replace_win32_incompat("c:\\test\\te\"st/2"),
|
||||
"c:\\test\\te_st/2")
|
||||
self.assertEqual(util.replace_win32_incompat("c:\\test\\d:/2"),
|
||||
"c:\\test\\d_/2")
|
||||
|
||||
@unittest.skipUnless(sys.platform != "win32", "non-windows test")
|
||||
@unittest.skipUnless(not IS_WIN, "non-windows test")
|
||||
def test_correct_absolute_non_win32(self):
|
||||
self.assertEqual(util.replace_win32_incompat("/test/te\"st/2"),
|
||||
"/test/te_st/2")
|
||||
@@ -89,7 +89,7 @@ class FormatTimeTest(PicardTestCase):
|
||||
|
||||
class HiddenFileTest(PicardTestCase):
|
||||
|
||||
@unittest.skipUnless(sys.platform != "win32", "non-windows test")
|
||||
@unittest.skipUnless(not IS_WIN, "non-windows test")
|
||||
def test(self):
|
||||
self.assertTrue(util.is_hidden('/a/b/.c.mp3'))
|
||||
self.assertTrue(util.is_hidden('/a/.b/.c.mp3'))
|
||||
|
||||
Reference in New Issue
Block a user