Giter VIP home page Giter VIP logo

discogs_client's People

Contributors

alifhughes avatar anssiahola avatar brephophagist avatar brunal avatar cburmeister avatar colinmarc avatar daanklijn avatar doomb0t avatar egh avatar funkyfuture avatar hellricer avatar joj0 avatar koobs avatar lemonase avatar leo-dor avatar mbortnyck avatar murdos avatar prcutler avatar rodneykeeling avatar sampsyo avatar susansalkeld avatar tenuki avatar vinylvault avatar vreon avatar wesrog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

discogs_client's Issues

What is most efficient way to filter a users's inventory?

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.

Test failures on Python 3.11

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 ===============================================

Intermittent issues accessing release.data items

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?

Local caching?

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?

Search returns non-existent release ids

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

Installation instructions update

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.

python3 cant view contents of <list>

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

Error 400 still occurring on some configurations

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)

Documentation Clarification Request: Master vs Main Release vs Release vs Track

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?

  1. Get all artist alias objects. Artist aliases have a different id and return a different set of releases**!**
  2. Get all releases (master or otherwise) for all artist aliases (main included)
  3. Recursively follow all release links (master --> main_release, master --> versions) to make sure we aren't missing any (and that discogs doesn't accidentally have a missed entries in the DB) and keep track of the link tree
  4. Create a messy graph mapping "master" tracks to all their versions (including main release) each of which has 1 or more album/releases?

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).

Improve/Cleanup test suite

  1. Separate tests into smaller suites/files, i.e. Client tests from test_models.py to test_client.py
  2. Improve coverage
  3. Add/Improve tests
  4. Mention in "Contributing" section about writing tests for new features?

Document class attributes so they render nicely in our Sphinx docs.

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.

Accessing release catalog number

Discussed in #37

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"]

Pagination above 100 disabled for inventories besides your own

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..

Add community related details to Release

Discussed in #101

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

Search in README example is not working.

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'

Collection items by release

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.

Concern

A "grown up" collection, will be more difficult to iterate and delete.

Possible solution

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.

Implementation

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

Problems with OAuth

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?

Null Attributes should not be excluded from the Release Object

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

python3 cant view contents of <list>

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

Prevent unnecessary requests when accessing fields that are not set

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:

  • specifying which fields can be returned when fetching from a specific endpoint, and then setting them to None (or similar) when they are not returned
  • cache the response of the first request

Can PayPal transaction IDs be accessed?

Hello! 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!

[FEATURE REQUEST] Handle HTTP 429 rate limit statuses

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!

Returned number of search items do not match the count attribute of the class

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!

Ability to set media type

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.

Barcode

I'm making an application, I can't get the barcode out of the release. Any suggestions Thank you

Issue with dicts that come from search function

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!

TypeError: sequence item 0: expected str instance, bytes found

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

#79

getting master by id raises json decode error

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?

You are making requests too quickly.

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>')

requests need timeout

For time-critical applications, it would be great if all of the request calls could use a provided timeout.

Errors and warnings on python 3.12

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.