mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 21:14:19 +01:00
Merge branch 'master' into Stunner-master
This commit is contained in:
commit
74df2788c0
16 changed files with 275 additions and 40 deletions
26
beets/__main__.py
Normal file
26
beets/__main__.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of beets.
|
||||
# Copyright 2017, Adrian Sampson.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""The __main__ module lets you run the beets CLI interface by typing
|
||||
`python -m beets`.
|
||||
"""
|
||||
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import sys
|
||||
from .ui import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
|
|
@ -988,7 +988,7 @@ class ArchiveImportTask(SentinelImportTask):
|
|||
`toppath` to that directory.
|
||||
"""
|
||||
for path_test, handler_class in self.handlers():
|
||||
if path_test(self.toppath):
|
||||
if path_test(util.py3_path(self.toppath)):
|
||||
break
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -126,8 +126,7 @@ def print_(*strings, **kwargs):
|
|||
Python 3.
|
||||
|
||||
The `end` keyword argument behaves similarly to the built-in `print`
|
||||
(it defaults to a newline). The value should have the same string
|
||||
type as the arguments.
|
||||
(it defaults to a newline).
|
||||
"""
|
||||
if not strings:
|
||||
strings = [u'']
|
||||
|
|
@ -136,11 +135,23 @@ def print_(*strings, **kwargs):
|
|||
txt = u' '.join(strings)
|
||||
txt += kwargs.get('end', u'\n')
|
||||
|
||||
# Send bytes to the stdout stream on Python 2.
|
||||
# Encode the string and write it to stdout.
|
||||
if six.PY2:
|
||||
txt = txt.encode(_out_encoding(), 'replace')
|
||||
|
||||
sys.stdout.write(txt)
|
||||
# On Python 2, sys.stdout expects bytes.
|
||||
out = txt.encode(_out_encoding(), 'replace')
|
||||
sys.stdout.write(out)
|
||||
else:
|
||||
# On Python 3, sys.stdout expects text strings and uses the
|
||||
# exception-throwing encoding error policy. To avoid throwing
|
||||
# errors and use our configurable encoding override, we use the
|
||||
# underlying bytes buffer instead.
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
out = txt.encode(_out_encoding(), 'replace')
|
||||
sys.stdout.buffer.write(out)
|
||||
else:
|
||||
# In our test harnesses (e.g., DummyOut), sys.stdout.buffer
|
||||
# does not exist. We instead just record the text string.
|
||||
sys.stdout.write(txt)
|
||||
|
||||
|
||||
# Configuration wrappers.
|
||||
|
|
|
|||
|
|
@ -161,7 +161,8 @@ class BeatportClient(object):
|
|||
:returns: Tracks in the matching release
|
||||
:rtype: list of :py:class:`BeatportTrack`
|
||||
"""
|
||||
response = self._get('/catalog/3/tracks', releaseId=beatport_id)
|
||||
response = self._get('/catalog/3/tracks', releaseId=beatport_id,
|
||||
perPage=100)
|
||||
return [BeatportTrack(t) for t in response]
|
||||
|
||||
def get_track(self, beatport_id):
|
||||
|
|
|
|||
|
|
@ -54,9 +54,11 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
'apisecret': 'plxtUTqoCzwxZpqdPysCwGuBSmZNdZVy',
|
||||
'tokenfile': 'discogs_token.json',
|
||||
'source_weight': 0.5,
|
||||
'user_token': '',
|
||||
})
|
||||
self.config['apikey'].redact = True
|
||||
self.config['apisecret'].redact = True
|
||||
self.config['user_token'].redact = True
|
||||
self.discogs_client = None
|
||||
self.register_listener('import_begin', self.setup)
|
||||
|
||||
|
|
@ -66,6 +68,12 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
c_key = self.config['apikey'].as_str()
|
||||
c_secret = self.config['apisecret'].as_str()
|
||||
|
||||
# Try using a configured user token (bypassing OAuth login).
|
||||
user_token = self.config['user_token'].as_str()
|
||||
if user_token:
|
||||
self.discogs_client = Client(USER_AGENT, user_token=user_token)
|
||||
return
|
||||
|
||||
# Get the OAuth token from a file or log in.
|
||||
try:
|
||||
with open(self._tokenfile()) as f:
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import shlex
|
|||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.ui import decargs, print_, Subcommand, UserError
|
||||
from beets.util import command_output, displayable_path, subprocess
|
||||
from beets.util import command_output, displayable_path, subprocess, \
|
||||
bytestring_path
|
||||
from beets.library import Item, Album
|
||||
import six
|
||||
|
||||
|
|
@ -112,14 +113,14 @@ class DuplicatesPlugin(BeetsPlugin):
|
|||
self.config.set_args(opts)
|
||||
album = self.config['album'].get(bool)
|
||||
checksum = self.config['checksum'].get(str)
|
||||
copy = self.config['copy'].get(str)
|
||||
copy = bytestring_path(self.config['copy'].as_str())
|
||||
count = self.config['count'].get(bool)
|
||||
delete = self.config['delete'].get(bool)
|
||||
fmt = self.config['format'].get(str)
|
||||
full = self.config['full'].get(bool)
|
||||
keys = self.config['keys'].as_str_seq()
|
||||
merge = self.config['merge'].get(bool)
|
||||
move = self.config['move'].get(str)
|
||||
move = bytestring_path(self.config['move'].as_str())
|
||||
path = self.config['path'].get(bool)
|
||||
tiebreak = self.config['tiebreak'].get(dict)
|
||||
strict = self.config['strict'].get(bool)
|
||||
|
|
|
|||
87
beetsplug/kodiupdate.py
Normal file
87
beetsplug/kodiupdate.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of beets.
|
||||
# Copyright 2017, Pauli Kettunen.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""Updates a Kodi library whenever the beets library is changed.
|
||||
This is based on the Plex Update plugin.
|
||||
|
||||
Put something like the following in your config.yaml to configure:
|
||||
kodi:
|
||||
host: localhost
|
||||
port: 8080
|
||||
user: user
|
||||
pwd: secret
|
||||
"""
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import requests
|
||||
from beets import config
|
||||
from beets.plugins import BeetsPlugin
|
||||
|
||||
|
||||
def update_kodi(host, port, user, password):
|
||||
"""Sends request to the Kodi api to start a library refresh.
|
||||
"""
|
||||
url = "http://{0}:{1}/jsonrpc/".format(host, port)
|
||||
|
||||
"""Content-Type: application/json is mandatory
|
||||
according to the kodi jsonrpc documentation"""
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
# Create the payload. Id seems to be mandatory.
|
||||
payload = {'jsonrpc': '2.0', 'method': 'AudioLibrary.Scan', 'id': 1}
|
||||
r = requests.post(
|
||||
url,
|
||||
auth=(user, password),
|
||||
json=payload,
|
||||
headers=headers)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
class KodiUpdate(BeetsPlugin):
|
||||
def __init__(self):
|
||||
super(KodiUpdate, self).__init__()
|
||||
|
||||
# Adding defaults.
|
||||
config['kodi'].add({
|
||||
u'host': u'localhost',
|
||||
u'port': 8080,
|
||||
u'user': u'kodi',
|
||||
u'pwd': u'kodi'})
|
||||
|
||||
config['kodi']['pwd'].redact = True
|
||||
self.register_listener('database_change', self.listen_for_db_change)
|
||||
|
||||
def listen_for_db_change(self, lib, model):
|
||||
"""Listens for beets db change and register the update"""
|
||||
self.register_listener('cli_exit', self.update)
|
||||
|
||||
def update(self, lib):
|
||||
"""When the client exists try to send refresh request to Kodi server.
|
||||
"""
|
||||
self._log.info(u'Updating Kodi library...')
|
||||
|
||||
# Try to send update request.
|
||||
try:
|
||||
update_kodi(
|
||||
config['kodi']['host'].get(),
|
||||
config['kodi']['port'].get(),
|
||||
config['kodi']['user'].get(),
|
||||
config['kodi']['pwd'].get())
|
||||
self._log.info(u'... started.')
|
||||
|
||||
except requests.exceptions.RequestException:
|
||||
self._log.warning(u'Update failed.')
|
||||
|
|
@ -56,5 +56,5 @@ class MBSubmitPlugin(BeetsPlugin):
|
|||
return [PromptChoice(u'p', u'Print tracks', self.print_tracks)]
|
||||
|
||||
def print_tracks(self, session, task):
|
||||
for i in task.items:
|
||||
for i in sorted(task.items, key=lambda i: i.track):
|
||||
print_data(None, i, self.config['format'].as_str())
|
||||
|
|
|
|||
|
|
@ -289,4 +289,10 @@ class GioURI(URIGetter):
|
|||
raise
|
||||
finally:
|
||||
self.libgio.g_free(uri_ptr)
|
||||
return uri
|
||||
|
||||
try:
|
||||
return uri.decode(util._fsencoding())
|
||||
except UnicodeDecodeError:
|
||||
raise RuntimeError(
|
||||
"Could not decode filename from GIO: {!r}".format(uri)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,8 +31,11 @@ New features:
|
|||
* A new :ref:`hardlink` config option instructs the importer to create hard
|
||||
links on filesystems that support them. Thanks to :user:`jacobwgillespie`.
|
||||
:bug:`2445`
|
||||
* :doc:`/plugins/embedart` by default now asks for confirmation before
|
||||
* :doc:`/plugins/embedart` by default now asks for confirmation before
|
||||
embedding art into music files. Thanks to :user:`Stunner`. :bug:`1999`
|
||||
* You can now run beets by typing `python -m beets`. :bug:`2453`
|
||||
* A new :doc:`/plugins/kodiupdate` lets you keep your Kodi library in sync
|
||||
with beets. Thanks to :user:`Pauligrinder`. :bug:`2411`
|
||||
|
||||
Fixes:
|
||||
|
||||
|
|
@ -50,6 +53,21 @@ Fixes:
|
|||
command is not found or exists with an error. :bug:`2430` :bug:`2433`
|
||||
* :doc:`/plugins/lyrics`: The Google search backend no longer crashes when the
|
||||
server responds with an error. :bug:`2437`
|
||||
* :doc:`/plugins/discogs`: You can now authenticate with Discogs using a
|
||||
personal access token. :bug:`2447`
|
||||
* Fix Python 3 compatibility when extracting rar archives in the importer.
|
||||
Thanks to :user:`Lompik`. :bug:`2443` :bug:`2448`
|
||||
* :doc:`/plugins/duplicates`: Fix Python 3 compatibility when using the
|
||||
``copy`` and ``move`` options. :bug:`2444`
|
||||
* :doc:`/plugins/mbsubmit`: The tracks are now sorted. Thanks to
|
||||
:user:`awesomer`. :bug:`2457`
|
||||
* :doc:`/plugins/thumbnails`: Fix a string-related crash on Python 3.
|
||||
:bug:`2466`
|
||||
* :doc:`/plugins/beatport`: More than just 10 songs are now fetched per album.
|
||||
:bug:`2469`
|
||||
* On Python 3, the :ref:`terminal_encoding` setting is respected again for
|
||||
output and printing will no longer crash on systems configured with a
|
||||
limited encoding.
|
||||
|
||||
|
||||
1.4.3 (January 9, 2017)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ into this if you've installed Python yourself with `Homebrew`_ or otherwise.)
|
|||
|
||||
If this happens, you can install beets for the current user only (sans
|
||||
``sudo``) by typing ``pip install --user beets``. If you do that, you might want
|
||||
to add ``~/Library/Python/2.7/bin`` to your ``$PATH``.
|
||||
to add ``~/Library/Python/3.6/bin`` to your ``$PATH``.
|
||||
|
||||
.. _System Integrity Protection: https://support.apple.com/en-us/HT204899
|
||||
.. _Homebrew: http://brew.sh
|
||||
|
|
@ -93,28 +93,28 @@ Installing on Windows
|
|||
Installing beets on Windows can be tricky. Following these steps might help you
|
||||
get it right:
|
||||
|
||||
1. If you don't have it, `install Python`_ (you want Python 2.7).
|
||||
1. If you don't have it, `install Python`_ (you want Python 3.6). The
|
||||
installer should give you the option to "add Python to PATH." Check this
|
||||
box. If you do that, you can skip the next step.
|
||||
|
||||
2. If you haven't done so already, set your ``PATH`` environment variable to
|
||||
include Python and its scripts. To do so, you have to get the "Properties"
|
||||
window for "My Computer", then choose the "Advanced" tab, then hit the
|
||||
"Environment Variables" button, and then look for the ``PATH`` variable in
|
||||
the table. Add the following to the end of the variable's value:
|
||||
``;C:\Python27;C:\Python27\Scripts``.
|
||||
``;C:\Python36;C:\Python36\Scripts``. You may need to adjust these paths to
|
||||
point to your Python installation.
|
||||
|
||||
3. Next, `install pip`_ (if you don't have it already) by downloading and
|
||||
running the `get-pip.py`_ script.
|
||||
3. Now install beets by running: ``pip install beets``
|
||||
|
||||
4. Now install beets by running: ``pip install beets``
|
||||
|
||||
5. You're all set! Type ``beet`` at the command prompt to make sure everything's
|
||||
4. You're all set! Type ``beet`` at the command prompt to make sure everything's
|
||||
in order.
|
||||
|
||||
Windows users may also want to install a context menu item for importing files
|
||||
into beets. Just download and open `beets.reg`_ to add the necessary keys to the
|
||||
registry. You can then right-click a directory and choose "Import with beets".
|
||||
If Python is in a nonstandard location on your system, you may have to edit the
|
||||
command path manually.
|
||||
into beets. Download the `beets.reg`_ file and open it in a text file to make
|
||||
sure the paths to Python match your system. Then double-click the file add the
|
||||
necessary keys to your registry. You can then right-click a directory and
|
||||
choose "Import with beets".
|
||||
|
||||
Because I don't use Windows myself, I may have missed something. If you have
|
||||
trouble or you have more detail to contribute here, please direct it to
|
||||
|
|
@ -142,8 +142,8 @@ place to start::
|
|||
Change that first path to a directory where you'd like to keep your music. Then,
|
||||
for ``library``, choose a good place to keep a database file that keeps an index
|
||||
of your music. (The config's format is `YAML`_. You'll want to configure your
|
||||
text editor to use spaces, not real tabs, for indentation.)
|
||||
|
||||
text editor to use spaces, not real tabs, for indentation. Also, ``~`` means
|
||||
your home directory in these paths, even on Windows.)
|
||||
|
||||
The default configuration assumes you want to start a new organized music folder
|
||||
(that ``directory`` above) and that you'll *copy* cleaned-up music into that
|
||||
|
|
@ -238,21 +238,30 @@ songs. Thus::
|
|||
$ beet ls album:bird
|
||||
The Mae Shi - Terrorbird - Revelation Six
|
||||
|
||||
As you can see, search terms by default search all attributes of songs. (They're
|
||||
By default, a search term will match any of a handful of :ref:`common
|
||||
attributes <keywordquery>` of songs.
|
||||
(They're
|
||||
also implicitly joined by ANDs: a track must match *all* criteria in order to
|
||||
match the query.) To narrow a search term to a particular metadata field, just
|
||||
put the field before the term, separated by a : character. So ``album:bird``
|
||||
only looks for ``bird`` in the "album" field of your songs. (Need to know more?
|
||||
:doc:`/reference/query/` will answer all your questions.)
|
||||
|
||||
The ``beet list`` command has another useful option worth mentioning, ``-a``,
|
||||
which searches for albums instead of songs::
|
||||
The ``beet list`` command also has an ``-a`` option, which searches for albums instead of songs::
|
||||
|
||||
$ beet ls -a forever
|
||||
Bon Iver - For Emma, Forever Ago
|
||||
Freezepop - Freezepop Forever
|
||||
|
||||
So handy!
|
||||
There's also an ``-f`` option (for *format*) that lets you specify what gets displayed in the results of a search::
|
||||
|
||||
$ beet ls -a forever -f "[$format] $album ($year) - $artist - $title"
|
||||
[MP3] For Emma, Forever Ago (2009) - Bon Iver - Flume
|
||||
[AAC] Freezepop Forever (2011) - Freezepop - Harebrained Scheme
|
||||
|
||||
In the format option, field references like `$format` and `$year` are filled
|
||||
in with data from each result. You can see a full list of available fields by
|
||||
running ``beet fields``.
|
||||
|
||||
Beets also has a ``stats`` command, just in case you want to see how much music
|
||||
you have::
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@ To use the ``discogs`` plugin, first enable it in your configuration (see
|
|||
|
||||
pip install discogs-client
|
||||
|
||||
You will also need to register for a `Discogs`_ account. The first time you
|
||||
run the :ref:`import-cmd` command after enabling the plugin, it will ask you
|
||||
to authorize with Discogs by visiting the site in a browser. Subsequent runs
|
||||
will not require re-authorization.
|
||||
You will also need to register for a `Discogs`_ account, and provide
|
||||
authentication credentials via a personal access token or an OAuth2
|
||||
authorization.
|
||||
|
||||
Matches from Discogs will now show up during import alongside matches from
|
||||
MusicBrainz.
|
||||
|
|
@ -25,6 +24,25 @@ MusicBrainz.
|
|||
If you have a Discogs ID for an album you want to tag, you can also enter it
|
||||
at the "enter Id" prompt in the importer.
|
||||
|
||||
OAuth Authorization
|
||||
```````````````````
|
||||
|
||||
The first time you run the :ref:`import-cmd` command after enabling the plugin,
|
||||
it will ask you to authorize with Discogs by visiting the site in a browser.
|
||||
Subsequent runs will not require re-authorization.
|
||||
|
||||
Authentication via Personal Access Token
|
||||
````````````````````````````````````````
|
||||
|
||||
As an alternative to OAuth, you can get a token from Discogs and add it to
|
||||
your configuration.
|
||||
To get a personal access token (called a "user token" in the `discogs-client`_
|
||||
documentation), login to `Discogs`_, and visit the
|
||||
`Developer settings page
|
||||
<https://www.discogs.com/settings/developers>`_. Press the ``Generate new
|
||||
token`` button, and place the generated token in your configuration, as the
|
||||
``user_token`` config option in the ``discogs`` section.
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ like this::
|
|||
inline
|
||||
ipfs
|
||||
keyfinder
|
||||
kodiupdate
|
||||
lastgenre
|
||||
lastimport
|
||||
lyrics
|
||||
|
|
@ -148,6 +149,8 @@ Interoperability
|
|||
* :doc:`embyupdate`: Automatically notifies `Emby`_ whenever the beets library changes.
|
||||
* :doc:`importfeeds`: Keep track of imported files via ``.m3u`` playlist file(s) or symlinks.
|
||||
* :doc:`ipfs`: Import libraries from friends and get albums from them via ipfs.
|
||||
* :doc:`kodiupdate`: Automatically notifies `Kodi`_ whenever the beets library
|
||||
changes.
|
||||
* :doc:`mpdupdate`: Automatically notifies `MPD`_ whenever the beets library
|
||||
changes.
|
||||
* :doc:`play`: Play beets queries in your music player.
|
||||
|
|
@ -160,6 +163,7 @@ Interoperability
|
|||
|
||||
.. _Emby: http://emby.media
|
||||
.. _Plex: http://plex.tv
|
||||
.. _Kodi: http://kodi.tv
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
|
|
|||
44
docs/plugins/kodiupdate.rst
Normal file
44
docs/plugins/kodiupdate.rst
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
KodiUpdate Plugin
|
||||
=================
|
||||
|
||||
The ``kodiupdate`` plugin lets you automatically update `Kodi`_'s music
|
||||
library whenever you change your beets library.
|
||||
|
||||
To use ``kodiupdate`` plugin, enable it in your configuration
|
||||
(see :ref:`using-plugins`).
|
||||
Then, you'll want to configure the specifics of your Kodi host.
|
||||
You can do that using a ``kodi:`` section in your ``config.yaml``,
|
||||
which looks like this::
|
||||
|
||||
kodi:
|
||||
host: localhost
|
||||
port: 8080
|
||||
user: kodi
|
||||
pwd: kodi
|
||||
|
||||
To use the ``kodiupdate`` plugin you need to install the `requests`_ library with::
|
||||
|
||||
pip install requests
|
||||
|
||||
You'll also need to enable JSON-RPC in Kodi in order the use the plugin.
|
||||
In Kodi's interface, navigate to System/Settings/Network/Services and choose "Allow control of Kodi via HTTP."
|
||||
|
||||
With that all in place, you'll see beets send the "update" command to your Kodi
|
||||
host every time you change your beets library.
|
||||
|
||||
.. _Kodi: http://kodi.tv/
|
||||
.. _requests: http://docs.python-requests.org/en/latest/
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The available options under the ``kodi:`` section are:
|
||||
|
||||
- **host**: The Kodi host name.
|
||||
Default: ``localhost``
|
||||
- **port**: The Kodi host port.
|
||||
Default: 8080
|
||||
- **user**: The Kodi host user.
|
||||
Default: ``kodi``
|
||||
- **pwd**: The Kodi host password.
|
||||
Default: ``kodi``
|
||||
|
|
@ -6,6 +6,8 @@ searches that select tracks and albums from your library. This page explains the
|
|||
query string syntax, which is meant to vaguely resemble the syntax used by Web
|
||||
search engines.
|
||||
|
||||
.. _keywordquery:
|
||||
|
||||
Keyword
|
||||
-------
|
||||
|
||||
|
|
|
|||
|
|
@ -276,12 +276,12 @@ class ThumbnailsTest(unittest.TestCase, TestHelper):
|
|||
if not gio.available:
|
||||
self.skipTest(u"GIO library not found")
|
||||
|
||||
self.assertEqual(gio.uri(u"/foo"), b"file:///") # silent fail
|
||||
self.assertEqual(gio.uri(b"/foo"), b"file:///foo")
|
||||
self.assertEqual(gio.uri(b"/foo!"), b"file:///foo!")
|
||||
self.assertEqual(gio.uri(u"/foo"), u"file:///") # silent fail
|
||||
self.assertEqual(gio.uri(b"/foo"), u"file:///foo")
|
||||
self.assertEqual(gio.uri(b"/foo!"), u"file:///foo!")
|
||||
self.assertEqual(
|
||||
gio.uri(b'/music/\xec\x8b\xb8\xec\x9d\xb4'),
|
||||
b'file:///music/%EC%8B%B8%EC%9D%B4')
|
||||
u'file:///music/%EC%8B%B8%EC%9D%B4')
|
||||
|
||||
|
||||
def suite():
|
||||
|
|
|
|||
Loading…
Reference in a new issue