joalla / discogs_client Goto Github PK
View Code? Open in Web Editor NEWContinuation of the "Official Python Client for the Discogs API"
Home Page: https://python3-discogs-client.readthedocs.io
License: Other
Continuation of the "Official Python Client for the Discogs API"
Home Page: https://python3-discogs-client.readthedocs.io
License: Other
Lets say I want to get a list of all the "Vinyl" a user has in their inventory. I can loop through user.inventory and check if Format == "Vinyl" but this uses the API for each one, and for most sellers they can have 1000's so that would take a long time. Is there an faster way to get this data? Like first being able filter for the format and/or genre?
Thanks.
Asking for .genres after a search always requires a second api call, I think I have tracked this down to
https://www.discogs.com/developers#page:database,header:database-search
has the key as 'genre' and
https://www.discogs.com/developers#page:database,header:database-release
has the key 'genres'
(Both are arrays)
If you want my hacky fix for this issue in the vein of #144, let me know.
Two tests are failing with Python 3.11.0b3. I think it's due to changed enum
repr:
========================================================= test session starts =========================================================
platform linux -- Python 3.11.0b3, pytest-7.1.2, pluggy-1.0.0
rootdir: /tmp/discogs_client
collected 45 items
discogs_client/tests/test_core.py ........ [ 17%]
discogs_client/tests/test_fetchers.py .. [ 22%]
discogs_client/tests/test_models.py ............................ [ 84%]
discogs_client/tests/test_utils.py .F...F. [100%]
============================================================== FAILURES ===============================================================
____________________________________________________ UtilsTestCase.test_condition _____________________________________________________
self = <discogs_client.tests.test_utils.UtilsTestCase testMethod=test_condition>
def test_condition(self):
self.assertRaises(TypeError, lambda: utils.Condition())
> self.assertEqual(utils.Condition.MINT, 'Mint (M)')
E AssertionError: <Condition.MINT: 'Mint (M)'> != 'Mint (M)'
discogs_client/tests/test_utils.py:66: AssertionError
______________________________________________________ UtilsTestCase.test_status ______________________________________________________
self = <discogs_client.tests.test_utils.UtilsTestCase testMethod=test_status>
def test_status(self):
self.assertRaises(TypeError, lambda: utils.Status())
> self.assertEqual(utils.Status.DRAFT, 'Draft')
E AssertionError: <Status.DRAFT: 'Draft'> != 'Draft'
discogs_client/tests/test_utils.py:71: AssertionError
========================================================== warnings summary ===========================================================
discogs_client/utils.py:102
/tmp/discogs_client/discogs_client/utils.py:102: DeprecationWarning: In 3.13 classes created inside an enum will not become a member. Use the `member` decorator to keep the current behavior.
class By(Enum):
discogs_client/utils.py:123
/tmp/discogs_client/discogs_client/utils.py:123: DeprecationWarning: In 3.13 classes created inside an enum will not become a member. Use the `member` decorator to keep the current behavior.
class Order(Enum):
.venv/lib/python3.11/site-packages/certifi/core.py:36
/tmp/discogs_client/.venv/lib/python3.11/site-packages/certifi/core.py:36: DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
_CACERT_CTX = get_path("certifi", "cacert.pem")
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================================================= short test summary info =======================================================
FAILED discogs_client/tests/test_utils.py::UtilsTestCase::test_condition - AssertionError: <Condition.MINT: 'Mint (M)'> != 'Mint (M)'
FAILED discogs_client/tests/test_utils.py::UtilsTestCase::test_status - AssertionError: <Status.DRAFT: 'Draft'> != 'Draft'
============================================== 2 failed, 43 passed, 3 warnings in 0.38s ===============================================
somewhere around the "Contribution" chapters
Hi, me again.
A while back I had this handy bit of code which was working great
query_dicts = [
{'release_id': release.id, 'format': release_format}
for release_format in release.data["format"]
if release_format in VALID_FORMATS
]
Given a release, this code is supposed to construct a list of dictionaries that look like this
[{'release_id': 44872, 'format': '12"'}, {'release_id': 44872, 'format': 'Vinyl'}, {'release_id': 44872, 'format': '45 RPM'}]
Suddenly - this stopped working. I'm not sure if it's because I switched from using release objects returned via an api search, to simply obtaining them via release_id lookup
but now I receive a persistent keyerror on release.data["format"]
. That said, release.data["formats"]
works, although instead of returning a list similar to this ['12"', 'Vinyl', '45 RPM']
it now returns a list of dicts that look like this [{'name': 'Vinyl', 'qty': '1', 'descriptions': ['12"', '45 RPM']}]
.
Annoying to say the least because now 'Vinyl' is separated from the rest of the formats in descriptions and they have to be combined.
No harm no foul - I simply rewrote the code to look something like this
query_dicts = []
combined_formats = set()
for release in releases:
for format_entry in release.data["formats"]:
combined_formats.add(format_entry["name"])
combined_formats.update(format_entry["descriptions"])
for release_format in combined_formats:
if release_format in VALID_FORMATS:
query_dicts.append({'release_id': release.id, 'format': release_format})
return query_dicts
which works - except now I am running into an extremely frustrating issue.
I am receiving a persistent key error on release.data["formats"]
. I had seen a similar issue in the past where it seemed that randomly "format"
would be available when formats
was not - but in this case, format
is never available, and formats
is intermittently available.
So I added a breakpoint within the exception clause of the below code:
try:
query_dicts = []
combined_formats = set()
for release in releases:
for format_entry in release.data["formats"]:
combined_formats.add(format_entry["name"])
combined_formats.update(format_entry["descriptions"])
for release_format in combined_formats:
if release_format in VALID_FORMATS:
query_dicts.append({'release_id': release.id, 'format': release_format})
return query_dicts
except KeyError as error:
return 'failed' <-------- breakpoint here
the error shows
KeyError('formats')
and yet, in the console, calling release.data["formats"]
returns without any issue whatsoever!
>>> release.data["formats"]
[{'name': 'Vinyl', 'qty': '1', 'descriptions': ['12"', '33 ⅓ RPM']}]
this leads me to believe there is a slight time delay before this data is available. I tried implementing a recursion approach to simply retry after a keyerror is encountered, but the timing issue appears to be significant enough that I am encountering maximum recursion depth errors.
I was only able to fix it by adding a release.refresh()
in the exception clause before retrying
Why is this happening?
It seems that regardless of caching each accessing each object's data
attribute is a function call as
rdata = release.data
raises an HTTPError
, which most likely happened since I was playing around with your objects trying to figure out how to best export them (since there isn't a to_json
method) and I hit my quota in the process...
Thoughts?
Came across this strange issue recently. This may not be an issue with the api client, but rather discogs' api itself.
It seems the search api can sometimes return "dormant" or otherwise hidden release ids which when attempting to access any real information about them results in a 404 error.
Here's an example:
https://www.discogs.com/release/6926394
As you can see the link opens up to a 404 page
This release_id was returned (along with others) from the following search:
>>> results = d.search("DOIN THE DO", type="release", artist="BETTY BOO", label="RHYTHM KING")
>>> len(results)
47
>>> for i, release in enumerate(results):
... print(release)
1 <Release 142429 "Betty Boo - Doin' The Do">
2 <Release 95314 "Betty Boo - Doin' The Do">
3 <Release 187294 "Betty Boo - Doin' The Do">
4 <Release 166172 "Betty Boo - Doin' The Do">
...
36 <Release 6926394 "Betty Boo - Doin' The Do"> # <-- BAD BOY
...
Calling
>>> results[36].data
{'country': 'UK', 'year': '1990', 'format': ['Vinyl', '12"', '45 RPM'], 'label': ['Rhythm King Records'], 'type': 'release', 'genre': ['Electronic', 'Pop'], 'style': ['House'], 'id': 6926394, 'barcode': ['5 016026 202392'], 'user_data': {'in_wantlist': False, 'in_collection': False}, 'master_id': 0, 'master_url': None, 'uri': '/Betty-Boo-Doin-The-Do/release/6926394', 'catno': 'LEFT 39T', 'title': "Betty Boo - Doin' The Do", 'thumb': '', 'cover_image': 'https://st.discogs.com/a33995aba384fd3cd0220d23d1e16c75f59d9927/images/spacer.gif', 'resource_url': 'https://api.discogs.com/releases/6926394', 'community': {'want': 0, 'have': 0}, 'format_quantity': 1, 'formats': [{'name': 'Vinyl', 'qty': '1', 'descriptions': ['12"', '45 RPM']}]}
will strangely yield the above results
but calling the refresh method
>>> results[36].refresh()
raise HTTPError(body['message'], status_code)
discogs_client.exceptions.HTTPError: 404: Release not found.
results in a 404
Furthermore, simply attempting to fetch the release by ID also results in a 404
>>> d.release(6926394)
raise HTTPError(body['message'], status_code)
discogs_client.exceptions.HTTPError: 404: Release not found.
I can only assume this is some strange deleted release or otherwise invalid release which is still being returned in search results
The installation instructions should be changed from:
$ pip install python3-discogs-client
to
$ pip3 install python3-discogs-client
On two systems ( centos 7 and raspbian ), I had to use pip3 . Using pip defaulted to Python 2.7 installation.
I am using a python discogs api package to retrieve a music playlist from online.
I need to iterate through this list to view all results, but no matter what I try I get a typeerror saying 'list object is not iterable / subscriptable'
d = discogs_client.Client('ExampleApplication/0.1', user_token=DISCOGS_TOKEN)
results = d.list('117796')
print('results=')
print(results)
print(results[0])
you can see I am returning a list just fine, but if I try to view the contents of said list, I get an error
results=
<List '117796' 'Ron Hardy @ Music Box'>
Traceback (most recent call last):
File "twitter_bot.py", line 52, in <module>
artist = results[0].artists[0]
TypeError: 'List' object is not subscriptable
There are apparently still issues with error 400 on post requests since #18. Seems to happen only on some setups for some reason - might be related to different versions of dependencies?
Python 3.8.3 & 3.9.1
requests 2.25.1
oauthlib 3.1.0
Test code:
import discogs_client
ds = discogs_client.Client('DevApp/1.0', user_token='...')
me = ds.identity()
me.wantlist.add(89411)
Hey!
I am new to the discogs_client
and api. I am trying to learn how to use it to wrangle together a discography information. Fundamentally I would like to get just all songs from a given artist, but I have, through deezer
, spotipy
, lyricsgenius
, ytmusicapi
and BeautifulSoup
quickly learned that oof this data is both messy and not standardized very well no matter where I look e.g. (Trackname about Thing
vs Trackname About Thing
vs Trackname (About Thing)
vs Trackname - About Thing
) and while this is can be mitigated through throughly scrubbing and normalizing names, it gets hard as not everyone separates what is a remix, cover, alternate version, extended version etc. All of this makes mapping songs to albums and even finding the base version of the song even more difficult, especially when combining apis.
I think Discogs has a nice approach. To my understanding (from the official API documentation)
Release
The Release resource represents a particular physical or digital object released by one or more Artists.
Master Release
The Master resource represents a set of similar Releases. Masters (also known as "master releases") have a "main release" which is often the chronologically earliest.
Master Release Versions
Retrieves a list of all Releases that are versions of this master. Accepts Pagination parameters.
Artist Releases
Returns a list of Releases and Masters associated with the Artist.
So regardless of EP, Single, or Album, if a generic entity was released for the first time a master version gets created which represents that unique entity, to which a "main version" is immediately created and tethered representing the first release (and subsequently when the master gets new versions they appear as part of the versions
list). Accordingly, a single (or a standalone track) , even if it is a master, has a tracklist
corresponding to list of one containing track data (which is mostly empty). Notably track
objects (in your api) do not have an id property
So my question, if I want to get the full discography from discogs the process should look something like this?
e.g.
| Track | Version | Album | Artists |
|--------------------------------|
| T1 | T1v1 | EP | A1 |
| T1 | T1v1 | EP | A2 |
| T1 | T1v1 | Alb1 | A1 |
| T1 | T1v1 | Alb1 | A2 |
| T1 | T1ext | Alb1 | A1 |
| T1 | T1ext | Alb1 | A2 |
So in this example there a "core" track T1 that has two versions T1v1 and T1ext, which has two artists and is on two different albums.
Question How to tell if a release is a single vs an EP?
I have a master track that has a trackless of just one. Do I just compare titles?
Or do I look at release.formats
[{'name': 'CDr', 'qty': '1', 'descriptions': ['Single', 'Promo']}]
and check if 'Single'
is in descriptions?
Not every release object (master / main / version) has a "type" (<-- if master) or "type_" (<--- if track in a trackless).
submitted in #91
test_models.py
to test_client.py
Add docs on using .fetch to get community data, such as contributors or ratings. See #101
Some settings in https://github.com/joalla/discogs_client/blob/master/docs/source/conf.py could possibly improved. The autogenerated Python package docs, provided by the Sphinx autodoc extension, somehow copy/inherit one docstring over and over again:
E.g: An attribute that determines its value....
is displayed over and over again:
https://python3-discogs-client.readthedocs.io/en/latest/discogs_client.html#discogs_client.models.CollectionFolder
A possible solution might be to put simple/short docstrings to some of these methods/properties directly and see if it renders in the autodocs and overrides these 'inherited' things.
Originally posted by masquinongy February 9, 2021
Hey, how do I get the catalog number for a release? Thanks.
It should be possible to access the catalog number of an release like so:
myrelease.labels[n].catno
Currently it's only possible to access it like that:
myrelease.labels[n].fetch("catno")
or
myrelease.labels[n].data["catno"]
discogs_client/discogs_client/fetchers.py
Lines 79 to 84 in 7dda350
I get this error when trying to print out more than 10,000 listings from a seller's inventory.
Here is the code snippet to reproduce the issue ( need to let it run for around 10 minutes ):
d = discogs_client.Client('Crate_Digger_Application_by_feacluster/0.1', user_token="xxx")
seller = d.user( 'philadelphiamusic' )
inventory = seller.inventory
inventory._per_page=100
count = 0
total = len(inventory)
print("<p>", total, " items found in seller's inventory <p>")
for album in inventory:
artist = album.data['release']['artist']
print ( artist + ' -- ' + str ( count ) + ' of ' + str ( total ) )
count +=1
Seems this issue has been reported here before:
https://www.discogs.com/forum/thread/778418
But wondering if there might be some workaround to get what I want via filtering? I am really only after VG+ or higher records. And only intersted in vinyl, not other formats. So that alone can drastically reduce a seller's inventory..
Originally posted by amoe June 9, 2022
Hi, great work on the client.How could I retrieve the average rating?
For instance, this page shows the rating: https://www.discogs.com/master/1837544-Netsky-Second-Nature
Which is 4.6 as of this writing.I'm also interested in how to fetch the number of users who have a master or release in their collection, does the API make this data available?
Following data is present in GET Release API point
"community": {
"contributors": [
{
"resource_url": "https://api.discogs.com/users/memory",
"username": "memory"
},
{
"resource_url": "https://api.discogs.com/users/_80_",
"username": "_80_"
}
],
"data_quality": "Correct",
"have": 252,
"rating": {
"average": 3.42,
"count": 45
},
"status": "Accepted",
"submitter": {
"resource_url": "https://api.discogs.com/users/memory",
"username": "memory"
},
"want": 42
}
I think at least have
, want
and rating
fields should be available through properties in Release
object
Example.
>>> print(release.want)
>>> 42
>>> print(release.have)
>>> 252
>>> print(release.rating)
>>> <Rating avg 3.42>
Couldn't find a way to get this information for Master releases so this would be for releases only
Using python 3.5.2 and python3-discogs-client==2.3.10
Steps:
import discogs_client
d = discogs_client.Client('ExampleApplication/0.1')
d = discogs_client.Client('ExampleApplication/0.1', user_token="{USERKEY}")
results = d.search('Stockholm By Night', type='release')
results.pages
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python3.5/dist-packages/discogs_client/models.py", line 328, in pages
self._load_pagination_info()
File "/usr/local/lib/python3.5/dist-packages/discogs_client/models.py", line 289, in _load_pagination_info
data = self.client._get(self._url_for_page(1))
File "/usr/local/lib/python3.5/dist-packages/discogs_client/client.py", line 113, in _get
return self._request('GET', url)
File "/usr/local/lib/python3.5/dist-packages/discogs_client/client.py", line 105, in _request
body = json.loads(content)
File "/usr/lib/python3.5/json/init.py", line 312, in loads
s.class.name))
TypeError: the JSON object must be str, not 'bytes'
Feature request for the next steps, when a collection "grows up"!
Trying to make ends meet with collection internals, this line of the docs helped a lot:
Because it’s possible to own more than one copy of a release, each with its own notes, grading, and so on, each instance of a release in a folder has an instance ID.
A "grown up" collection, will be more difficult to iterate and delete.
The API provides a url which returns only the "collection items" of a release_id
.
Each result, includes the instance_id
which is crucial for the deletion process.
If implemented, a user could do:
folder = me.collection_folders[0]
release_in_collection = folder.release(1234567)
# Option 1
for instance in release_in_collection:
if criterion_met:
folder.remove_release(release_in_collection, instance=instance['instance_id'])
#Option 1 + 1
folder.remove_release(release_in_collection, instance="__all__")
# Power Option
folder.remove_release(
release_id,
instances=[release_in_collection[0]['instance_id'], release_in_collection[1]['instance_id']],
) # Remove all if instances is None
I'm a tad stuck. Following the doc:
import discogs_client
ConsumerKey = "my_app_key"
ConsumerSecret = "my_app_secret"
client = discogs_client.Client("puddletag", consumer_key=ConsumerKey, consumer_secret=ConsumerSecret)
auth_url = client.get_authorize_url()
works wonderfully returning:
('my_request_token', 'my_request_secret', 'https://www.discogs.com/oauth/authorize?oauth_token=my_request_token')
after which the docs here:
https://python3-discogs-client.readthedocs.io/en/latest/authentication.html#oauth-authentication
fail me a little. I'm not sure what do do with my_request_secret
, but I can see my_request_token
is in the URL.
if I paste that URL into my browser, I actually get a code. Discogs returns:
Authorization successful
If the application asks you for a code, enter the following:
my_code
You can now close this window.
But if I now run this:
token = client.get_access_token(my_code)
An exception is raised with '400: Invalid response from access token URL.'
What am I doing wrong here? And is there something missing from the docs that might have helped me fix this?
I've come across a few instances where, say - the "country" attribute is missing from a release
Take this one for example:
https://www.discogs.com/release/18424870-The-Smashing-Pumpkins-Rotten-Apples-Greatest-Hits
In the case of the missing year, I'm still able to access the year attribute without a KeyError
>>> release.data["year"]
0
However when attempting to access the country attribute:
release.data["country"]
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydevd_bundle/pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<input>", line 1, in <module>
KeyError: 'country'
This makes it rather clunky to handle cases where certain attributes are missing - because in some cases after catching a KeyError, I simply need to call release.refresh()
and the keys may then become accessible (side note, would be nice if I didn't have to call release.refresh())
But if the key is truly missing, a value (such as 0 in the case of the year) can convey that.
I'm not sure if this is an api client issue or an issue from Discogs side
I am using a python discogs api package to retrieve a music playlist from online.
I need to iterate through this list to view all results, but no matter what I try I get a typeerror saying 'list object is not iterable / subscriptable'
d = discogs_client.Client('ExampleApplication/0.1', user_token=DISCOGS_TOKEN)
results = d.list('117796')
print('results=')
print(results)
print(results[0])
you can see I am returning a list just fine, but if I try to view the contents of said list, I get an error
results=
<List '117796' 'Ron Hardy @ Music Box'>
Traceback (most recent call last):
File "twitter_bot.py", line 52, in <module>
artist = results[0].artists[0]
TypeError: 'List' object is not subscriptable
When accessing fields which are not set in the Discogs database (such as the real_name
for an artist which is not a person), this can lead to an unnecessary extra request.
Take this example, where we first access the name, triggering the first request. Then we access the real_name
(which is not set for The Beatles, since it is not a person), triggering the second request to the same endpoint.
import discogs_client
import logging
logging.basicConfig(level=logging.DEBUG)
client = discogs_client.Client('ExampleApplication/0.1')
artist = client.artist(82730)
print(f'{artist.name=}')
print(f'{artist.real_name=}')
This results in the following:
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.discogs.com:443
DEBUG:urllib3.connectionpool:https://api.discogs.com:443 "GET /artists/82730 HTTP/1.1" 200 None
artist.name='The Beatles'
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.discogs.com:443
DEBUG:urllib3.connectionpool:https://api.discogs.com:443 "GET /artists/82730 HTTP/1.1" 200 None
artist.real_name=None
I think the problem lies in the fact that the Discogs API does not return a value (e.g. None
) when it is not set. And discogs_client
only sets/updates a value when it is explicitly returned by the API.
I can see two solutions:
None
(or similar) when they are not returnedHello! Thank you for the wonderful client!!
I'm able to retrieve most Order information, and I'm now trying to access PayPal transaction IDs, which now appear in the Order messages section like this:
{BUYER} sent payment of {AMOUNT} (Transaction #{PAYPAL_TRANSACTION_ID})
I can access this message like this:
import discogs_client as dc
# MY_DISCOGS_TOKEN = {a string}
# MY_DISCOGS_ID = {my user ID}
# ORDER_ID = {an order number (integer)}
client = dc.Client('PersonalDatabaseApplication/0.1', user_token=MY_DISCOGS_TOKEN)
order = client.order( f'{MY_DISCOGS_ID}-{ORDER_ID}' )
order.refresh()
msgs = order.messages
msg = msgs[3].message
print( msg )
but the result is just this:
{BUYER} sent payment of {AMOUNT}
with no PayPal transaction ID.
Is it possible to get the PayPal transaction ID another way?
Thanks!
Hey,
First of all thanks for the amazing work on this library, it makes really easy to interact with the discogs API. After building a POC for an app I have in mind, I stumbled upon the problem of rate limitations by Discogs in their v2 API. I implemented a backoff mechanism in my app to overcome this, but I think it would be a great addition to implement this in the client itself, maybe by passing an attribute to the Client
class so that users can control wether this is handled or not. I would also like to offer my help in order to implement this, but would like to hear your opinion on this first. Thanks!
Hi everyone!
I've noticed that the number of returned results from the search function doesn't match the count attribute of the class.
For example, the following code:
import discogs_client
client= discogs_client.Client(use_agent="USER_AGENT", user_token="USER_TOKEN")
results = client.search("Daft Punk One more time", type="master")
search_results_dicts = [result.data for result in results]
print(results.count == len(search_results_dicts))
print(results.count)
print(len(search_results_dicts))
would return:
>>> False
>>> 82
>>> 80
Interestingly, if you actually go to the Discogs webpage (in this case, that would be: https://www.discogs.com/search/?q=Daft+Punk+One+more+time&type=master) you will notice that the count specified is indeed 82 but if you count the items, you would only get to 80.
On the first page, you will see 1 out of 50 although there are only 48 items (6 items per row in 8 rows on PC) and the second one that has 32 items (from 51 to 82) and with the correct count.
Therefore, I believe that this comes from the Discogs API rather than the package itself.
Hope this help with further development!
It would be useful to be able to select which format to return for things such as artist bio. e.g., some applications would be better with plaintext+json or html+json over the default discogs+json.
I'm making an application, I can't get the barcode out of the release. Any suggestions Thank you
cause the old one's deprecated: https://blog.readthedocs.com/migrate-configuration-v2/
Deadline: Sep, 25th
Hi everyone!
There seems to be an issue with the dicts that are returned when using .data
method on the one of the objects resulting from the search
function.
For example, the following few lines of code should print the number of items in the dict:
import discogs_client
client = discogs_client.Client(use_agent="USER_AGENT", user_token="USER_TOKEN")
results = client.search("Daft Punk One more time", type="master")
print(len(results[0].data.items()))
print(len(results[1].data.items()))
And the output of this code is:
>>> 34
>>> 19
Oddly, it sometimes happens that all dict items have 19 key-value pairs, including the first one - without even changing the code.
Moreover, when inspecting these 2 dicts, I notice that the last key in the first result is "embed"
while in the second it's "community"
. And even more importantly for my use case, the "artists"
key is missing in the all the results except the first one.
I'm using version 2.6 of the python3-discogs-client
package with dedicated conda environment
using Python 3.8
.
Any help here would be appreciated!
Traceback (most recent call last):
File "/usr/bin/beet", line 11, in
load_entry_point('beets==1.4.6', 'console_scripts', 'beet')()
File "/usr/share/beets/beets/ui/init.py", line 1256, in main
_raw_main(args)
File "/usr/share/beets/beets/ui/init.py", line 1243, in _raw_main
subcommand.func(lib, suboptions, subargs)
File "/usr/share/beets/beets/ui/commands.py", line 937, in import_func
import_files(lib, paths, query)
File "/usr/share/beets/beets/ui/commands.py", line 914, in import_files
session.run()
File "/usr/share/beets/beets/importer.py", line 327, in run
pl.run_parallel(QUEUE_SIZE)
File "/usr/share/beets/beets/util/pipeline.py", line 445, in run_parallel
six.reraise(exc_info[0], exc_info[1], exc_info[2])
File "/usr/lib/python3/dist-packages/six.py", line 693, in reraise
raise value
File "/usr/share/beets/beets/util/pipeline.py", line 312, in run
out = self.coro.send(msg)
File "/usr/share/beets/beets/util/pipeline.py", line 194, in coro
func(*(args + (task,)))
File "/usr/share/beets/beets/importer.py", line 1346, in lookup_candidates
task.lookup_candidates()
File "/usr/share/beets/beets/importer.py", line 636, in lookup_candidates
autotag.tag_album(self.items, search_ids=self.search_ids)
File "/usr/share/beets/beets/autotag/match.py", line 460, in tag_album
va_likely):
File "/usr/share/beets/beets/plugins.py", line 496, in decorated
for v in generator(*args, **kwargs):
File "/usr/share/beets/beets/autotag/hooks.py", line 601, in album_candidates
for candidate in plugins.candidates(items, artist, album, va_likely):
File "/usr/share/beets/beets/plugins.py", line 355, in candidates
for candidate in plugin.candidates(items, artist, album, va_likely):
File "/usr/share/beets/beetsplug/discogs.py", line 151, in candidates
return self.get_albums(query)
File "/usr/share/beets/beetsplug/discogs.py", line 211, in get_albums
type='release').page(1)
File "/usr/local/lib/python3.6/dist-packages/discogs_client/client.py", line 134, in search
fields['q'] = ' '.join(query)
TypeError: sequence item 0: expected str instance, bytes found
python 3.6.9
A feature request:
I am searching for remove_release
as the counterpart of add_relese in a collection folder.
I am trying to get the master of this id:
https://www.discogs.com/master/392093-Childish-Gambino-Camp
discogs = discogs_client.Client(f"examplename/1.0")
discogs.master(392093)
but this raises json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
For other master's it seems to work at first:
>>> discogs.master("2482309")
<Master '2482309' 'Shroud'>
But if I access an attribute it fails anyways:
>>> discogs.master("2482309").main_release
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Can you replicate this or do you need more info?
I seem to randomnly get this error when searching a seller's inventory of more than a thousand or so albums. Here is the code snippet you can try ( just edit the line with the user_token )
import discogs_client
import sys, time, re, time, json
debug = 0
buyer = 'faraz12inch45rpm'
seller = 'rockdonkey'
format = 'LP'
condition = 'Mint|NM|VG+'
budget = 10000
condition = re.sub ( '\+', '\\+', condition )
#######################
def check_conditions():
global album, budget
if ( re.search ( format , album.data['release']['format'], re.IGNORECASE ) ):
if ( re.search ( condition , album.data['condition'] ) ):
if ( int ( album.data['original_price']['value'] ) > budget ):
return 0
else:
return 1
return 0
########
# MAIN #
########
d = discogs_client.Client('my_app/0.1', user_token="xxxxxx")
print('<table class=default_table CELLSPACING=0 CELLPADDING=10>')
print("<tr><td> Buyer: </td><td>", buyer, "</td></tr>")
print("<tr><td> Seller: </td><td>", seller, "</td></tr>")
print("<tr><td> Format: </td><td>", format, "</td></tr>")
print("<tr><td> Condition: </td><td>", condition, "</td></tr>")
print("<tr><td> Budget: </td><td>", budget, "</td></tr></table>")
me = d.user(buyer)
collection = me.collection_folders
all = collection[0].releases
if ( all.count < 2 ):
print("<p>No artists found in your collection. Try adding at least a few dozen albums from artists you like. The script will search those artists from the sellers inventory. ")
sys.exit()
print("<p>Total artists in my collection: ", all.count)
my_artists = []
for album in all:
artist = album.data['basic_information']['artists'][0]['name']
if ( re.search ( 'Various', artist ) ):
continue
if artist not in my_artists:
my_artists.append(artist)
print("<p>Unique artists in my collection: ", len( my_artists ))
with open('my_artists.json', 'w') as f:
f.write(json.dumps( my_artists ))
if debug:
for artist in my_artists:
print(artist)
########################
options = [
('listed', 'asc'),
('listed', 'desc'),
('price', 'asc'),
('price', 'desc'),
('item', 'asc'),
('item', 'desc'),
('artist', 'asc'),
('artist', 'desc'),
('label', 'asc'),
('label', 'desc'),
('catno', 'asc'),
('catno', 'desc'),
('audio', 'asc'),
('audio', 'desc'),
]
seller = d.user( seller )
inventory = seller.inventory
inventory._per_page=100
pages = inventory.pages
# Unique albums
found = []
count = 0
total = len(inventory)
print("<p>", total, " items found in seller's inventory <p>")
if ( total > 1000000 ):
print("<h3>Seller's inventory is too large: </h3>", total)
sys.exit()
if ( total > 10000 ):
print("<h3>Seller's inventory is too large. Job may crash before searching the first 10,000 </h3>", total)
print('<table class=default_table CELLSPACING=0 CELLPADDING=10>')
print('<tr> <th>Artist</th><th>Title</th> <th>Price</th> <th>Format</th> <th>Condition</th> <th> Label </th> <th> Year </th> </tr>')
for option in options:
# All albums found, exit
if len(found) == total:
exit()
# Change the sorting
inventory.sort(*option)
# Get First 100 pages (10,000 albums)
for page_num in range(1, min(100, pages) + 1):
for album in inventory.page(page_num):
# Find unique albums from page
if album.id not in found:
found.append(album.id)
artist = album.data['release']['artist']
if artist in my_artists:
if ( check_conditions() ):
print('<tr><td>', artist.encode('utf-8').strip().decode('utf8') , '</td>')
print('<td><a href=', album.data['uri'] , ' target=new>', album.data['release']['title'].encode('utf-8').strip().decode('utf8') , '</a></td>')
print('<td>', album.data['original_price']['value'], '</td>')
print('<td>', album.data['release']['format'], '</td>')
print('<td>', album.data['condition'], '</td>')
id = album.data['release']['id']
print('<td>', d.release(id).labels[0].data['name'].encode('utf-8').strip().decode('utf8'), '</td>')
print('<td>', d.release(id).year, '</td></tr>')
# print( '<tr><td colspan=5>' + f'{page_num}: {len(found)} / {total} found.' + '</td></tr>')
if ( len(found) % 1000 == 0 ):
print( '<tr><td colspan=5>' + f'{len(found)} / {total} found.' + '</td></tr>')
print('</tr>')
For time-critical applications, it would be great if all of the request calls could use a provided timeout.
pytest in cmd at 12:01:22
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.12.1, pytest-7.4.3, pluggy-1.3.0
rootdir: i:\Git\discogs_client
plugins: anyio-4.2.0, typeguard-4.1.5
collected 50 items
discogs_client\tests\test_core.py ............ [ 24%]
discogs_client\tests\test_fetchers.py .. [ 28%]
discogs_client\tests\test_models.py ...F......................... [ 86%]
discogs_client\tests\test_utils.py ....... [100%]
============================================================================================================== FAILURES ===============================================================================================================
________________________________________________________________________________________________ ModelsTestCase.test_collection_value _________________________________________________________________________________________________
self = <discogs_client.tests.test_models.ModelsTestCase testMethod=test_collection_value>
def test_collection_value(self):
"""Collection Value can be fetched and parsed"""
u = self.d.user("example")
self.assertEqual(isinstance(u.collection_value, CollectionValue), True)
> self.assertEqual(u.collection_value.minimum, "£1.05")
E AssertionError: '£1.05' != '£1.05'
E - £1.05
E ? -
E + £1.05
discogs_client\tests\test_models.py:351: AssertionError
========================================================================================================== warnings summary ===========================================================================================================
C:\Python3\Lib\site-packages\dateutil\tz\tz.py:37
C:\Python3\Lib\site-packages\dateutil\tz\tz.py:37: DeprecationWarning: datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.fromtimestamp(timestamp, datetime.UTC).
EPOCH = datetime.datetime.utcfromtimestamp(0)
discogs_client\utils.py:102
i:\Git\discogs_client\discogs_client\utils.py:102: DeprecationWarning: In 3.13 classes created inside an enum will not become a member. Use the `member` decorator to keep the current behavior.
class By(Enum):
discogs_client\utils.py:123
i:\Git\discogs_client\discogs_client\utils.py:123: DeprecationWarning: In 3.13 classes created inside an enum will not become a member. Use the `member` decorator to keep the current behavior.
class Order(Enum):
discogs_client\fetchers.py:227
i:\Git\discogs_client\discogs_client\fetchers.py:227: SyntaxWarning: invalid escape sequence '\w'
path_with_params = re.compile('(?P<dir>(\w+/)+)(?P<query>\w+)\?(?P<params>.*)')
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================================================================================================= short test summary info =======================================================================================================
FAILED discogs_client/tests/test_models.py::ModelsTestCase::test_collection_value - AssertionError: '£1.05' != '£1.05'
============================================================================================== 1 failed, 49 passed, 4 warnings in 8.81s ===============================================================================================
The enum warning might be fixed with a decorator.
I put the regex into regex101 but it says it is valid, will need to look deeper into regex warning.
The error could be a windows thing 😒
The timezone error I'll try to look at as well
Feature request: .index on collection_folders to get the index using the folder name. Maybe there is already a way to do it other than looping through all folders and checking their name attribute, but I'm not really advanced in Python. If there is no other way, I would be glad to at least try to implement this feature and create a pull request for it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.