discogs-sdk 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. discogs_sdk/__init__.py +134 -0
  2. discogs_sdk/_async/__init__.py +3 -0
  3. discogs_sdk/_async/_client.py +258 -0
  4. discogs_sdk/_async/_lazy.py +94 -0
  5. discogs_sdk/_async/_oauth.py +100 -0
  6. discogs_sdk/_async/_paginator.py +100 -0
  7. discogs_sdk/_async/_resource.py +92 -0
  8. discogs_sdk/_async/resources/__init__.py +75 -0
  9. discogs_sdk/_async/resources/artists.py +39 -0
  10. discogs_sdk/_async/resources/collection.py +248 -0
  11. discogs_sdk/_async/resources/exports.py +30 -0
  12. discogs_sdk/_async/resources/labels.py +32 -0
  13. discogs_sdk/_async/resources/lists.py +33 -0
  14. discogs_sdk/_async/resources/marketplace.py +122 -0
  15. discogs_sdk/_async/resources/masters.py +50 -0
  16. discogs_sdk/_async/resources/releases.py +96 -0
  17. discogs_sdk/_async/resources/search.py +22 -0
  18. discogs_sdk/_async/resources/uploads.py +35 -0
  19. discogs_sdk/_async/resources/users.py +168 -0
  20. discogs_sdk/_async/resources/wantlist.py +34 -0
  21. discogs_sdk/_base_client.py +177 -0
  22. discogs_sdk/_exceptions.py +60 -0
  23. discogs_sdk/_sync/__init__.py +6 -0
  24. discogs_sdk/_sync/_client.py +230 -0
  25. discogs_sdk/_sync/_lazy.py +72 -0
  26. discogs_sdk/_sync/_oauth.py +85 -0
  27. discogs_sdk/_sync/_paginator.py +89 -0
  28. discogs_sdk/_sync/_resource.py +66 -0
  29. discogs_sdk/_sync/resources/__init__.py +78 -0
  30. discogs_sdk/_sync/resources/artists.py +34 -0
  31. discogs_sdk/_sync/resources/collection.py +228 -0
  32. discogs_sdk/_sync/resources/exports.py +23 -0
  33. discogs_sdk/_sync/resources/labels.py +29 -0
  34. discogs_sdk/_sync/resources/lists.py +28 -0
  35. discogs_sdk/_sync/resources/marketplace.py +101 -0
  36. discogs_sdk/_sync/resources/masters.py +43 -0
  37. discogs_sdk/_sync/resources/releases.py +81 -0
  38. discogs_sdk/_sync/resources/search.py +19 -0
  39. discogs_sdk/_sync/resources/uploads.py +28 -0
  40. discogs_sdk/_sync/resources/users.py +160 -0
  41. discogs_sdk/_sync/resources/wantlist.py +30 -0
  42. discogs_sdk/models/__init__.py +109 -0
  43. discogs_sdk/models/_common.py +171 -0
  44. discogs_sdk/models/artist.py +34 -0
  45. discogs_sdk/models/collection.py +38 -0
  46. discogs_sdk/models/export.py +15 -0
  47. discogs_sdk/models/label.py +31 -0
  48. discogs_sdk/models/list_.py +38 -0
  49. discogs_sdk/models/marketplace.py +90 -0
  50. discogs_sdk/models/master.py +40 -0
  51. discogs_sdk/models/release.py +94 -0
  52. discogs_sdk/models/search.py +28 -0
  53. discogs_sdk/models/upload.py +17 -0
  54. discogs_sdk/models/user.py +46 -0
  55. discogs_sdk/models/wantlist.py +11 -0
  56. discogs_sdk/oauth.py +21 -0
  57. discogs_sdk/py.typed +0 -0
  58. discogs_sdk-0.1.0.dist-info/METADATA +339 -0
  59. discogs_sdk-0.1.0.dist-info/RECORD +60 -0
  60. discogs_sdk-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Any, TypeVar
5
+
6
+ import httpx
7
+ from pydantic import BaseModel
8
+
9
+ if TYPE_CHECKING:
10
+ from discogs_sdk._async._client import AsyncDiscogs
11
+
12
+ _M = TypeVar("_M", bound=BaseModel)
13
+
14
+
15
+ class AsyncAPIResource:
16
+ def __init__(self, client: AsyncDiscogs) -> None:
17
+ self._client = client
18
+
19
+ def _parse_list_response(self, response: httpx.Response, model_cls: type[_M], items_key: str) -> list[_M]:
20
+ """Parse JSON, raise on error, return validated list from a keyed array."""
21
+ data = response.json()
22
+ self._client._maybe_raise(response.status_code, data, retry_after=response.headers.get("Retry-After"))
23
+ return [model_cls.model_validate(item) for item in data.get(items_key, [])]
24
+
25
+ def _parse_response(self, response: httpx.Response, model_cls: type[_M]) -> _M:
26
+ """Parse JSON, raise on error, return validated model."""
27
+ data = response.json()
28
+ self._client._maybe_raise(response.status_code, data, retry_after=response.headers.get("Retry-After"))
29
+ return model_cls.model_validate(data)
30
+
31
+ def _raise_for_error(self, response: httpx.Response) -> None:
32
+ """Raise on error for void methods (delete, update returning None)."""
33
+ if response.status_code >= 400:
34
+ body = response.json() if response.content else {}
35
+ self._client._maybe_raise(response.status_code, body, retry_after=response.headers.get("Retry-After"))
36
+
37
+ async def _delete(self, path: str) -> httpx.Response:
38
+ return await self._request("DELETE", path)
39
+
40
+ async def _get(self, path: str, *, params: dict[str, Any] | None = None) -> httpx.Response:
41
+ return await self._request("GET", path, params=params)
42
+
43
+ async def _get_binary(self, path: str) -> bytes:
44
+ response = await self._client._send("GET", self._client._build_url(path))
45
+ self._client._maybe_raise(
46
+ response.status_code,
47
+ response.text,
48
+ retry_after=response.headers.get("Retry-After"),
49
+ )
50
+ return response.content
51
+
52
+ async def _post(
53
+ self,
54
+ path: str,
55
+ *,
56
+ json: dict[str, Any] | None = None,
57
+ params: dict[str, Any] | None = None,
58
+ ) -> httpx.Response:
59
+ return await self._request("POST", path, json=json, params=params)
60
+
61
+ async def _post_file(self, path: str, *, file_path: str) -> httpx.Response:
62
+ p = Path(file_path)
63
+ data = p.read_bytes()
64
+ return await self._client._send(
65
+ "POST",
66
+ self._client._build_url(path),
67
+ files={"upload": (p.name, data, "text/csv")},
68
+ )
69
+
70
+ async def _put(
71
+ self,
72
+ path: str,
73
+ *,
74
+ json: dict[str, Any] | None = None,
75
+ params: dict[str, Any] | None = None,
76
+ ) -> httpx.Response:
77
+ return await self._request("PUT", path, json=json, params=params)
78
+
79
+ async def _request(
80
+ self,
81
+ method: str,
82
+ path: str,
83
+ *,
84
+ json: dict[str, Any] | None = None,
85
+ params: dict[str, Any] | None = None,
86
+ ) -> httpx.Response:
87
+ return await self._client._send(
88
+ method,
89
+ self._client._build_url(path),
90
+ json=json,
91
+ params=params,
92
+ )
@@ -0,0 +1,75 @@
1
+ from discogs_sdk._async.resources.artists import ArtistReleases, Artists
2
+ from discogs_sdk._async.resources.collection import (
3
+ Collection,
4
+ CollectionFields,
5
+ CollectionFolders,
6
+ CollectionReleases,
7
+ CollectionValueResource,
8
+ FolderReleases,
9
+ )
10
+ from discogs_sdk._async.resources.exports import Exports
11
+ from discogs_sdk._async.resources.labels import LabelReleases, Labels
12
+ from discogs_sdk._async.resources.lists import Lists, UserLists
13
+ from discogs_sdk._async.resources.marketplace import (
14
+ Marketplace,
15
+ MarketplaceFee,
16
+ MarketplaceListings,
17
+ MarketplaceOrders,
18
+ OrderMessages,
19
+ )
20
+ from discogs_sdk._async.resources.masters import Masters, MasterVersions
21
+ from discogs_sdk._async.resources.releases import (
22
+ ReleaseMarketplaceStats,
23
+ ReleasePriceSuggestions,
24
+ ReleaseRating,
25
+ ReleaseStatsResource,
26
+ Releases,
27
+ )
28
+ from discogs_sdk._async.resources.search import SearchResource
29
+ from discogs_sdk._async.resources.uploads import Uploads
30
+ from discogs_sdk._async.resources.users import (
31
+ UserContributions,
32
+ UserInventory,
33
+ UserNamespace,
34
+ Users,
35
+ UserSubmissions,
36
+ UserUpdate,
37
+ )
38
+ from discogs_sdk._async.resources.wantlist import Wantlist
39
+
40
+ __all__ = [
41
+ "ArtistReleases",
42
+ "Artists",
43
+ "Collection",
44
+ "CollectionFields",
45
+ "CollectionFolders",
46
+ "CollectionReleases",
47
+ "CollectionValueResource",
48
+ "Exports",
49
+ "FolderReleases",
50
+ "LabelReleases",
51
+ "Labels",
52
+ "Lists",
53
+ "Marketplace",
54
+ "MarketplaceFee",
55
+ "MarketplaceListings",
56
+ "MarketplaceOrders",
57
+ "Masters",
58
+ "MasterVersions",
59
+ "OrderMessages",
60
+ "ReleasePriceSuggestions",
61
+ "ReleaseMarketplaceStats",
62
+ "ReleaseRating",
63
+ "ReleaseStatsResource",
64
+ "Releases",
65
+ "SearchResource",
66
+ "Uploads",
67
+ "UserContributions",
68
+ "UserInventory",
69
+ "UserLists",
70
+ "UserNamespace",
71
+ "Users",
72
+ "UserSubmissions",
73
+ "UserUpdate",
74
+ "Wantlist",
75
+ ]
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from discogs_sdk._async._lazy import AsyncLazyResource
4
+ from discogs_sdk._async._paginator import AsyncPage
5
+ from discogs_sdk._async._resource import AsyncAPIResource
6
+ from discogs_sdk.models.artist import Artist, ArtistRelease
7
+
8
+
9
+ class ArtistReleases(AsyncAPIResource):
10
+ def __init__(self, client, artist_id: int) -> None:
11
+ super().__init__(client)
12
+ self._artist_id = artist_id
13
+
14
+ def list(
15
+ self,
16
+ *,
17
+ sort: str | None = None,
18
+ sort_order: str | None = None,
19
+ ) -> AsyncPage[ArtistRelease]:
20
+ params = {k: v for k, v in {"sort": sort, "sort_order": sort_order}.items() if v}
21
+ return AsyncPage(
22
+ client=self._client,
23
+ path=f"/artists/{self._artist_id}/releases",
24
+ params=params,
25
+ model_cls=ArtistRelease,
26
+ items_key="releases",
27
+ )
28
+
29
+
30
+ class Artists(AsyncAPIResource):
31
+ def get(self, artist_id: int) -> AsyncLazyResource:
32
+ return AsyncLazyResource(
33
+ client=self._client,
34
+ path=f"/artists/{artist_id}",
35
+ model_cls=Artist,
36
+ sub_resources={
37
+ "releases": lambda: ArtistReleases(self._client, artist_id),
38
+ },
39
+ )
@@ -0,0 +1,248 @@
1
+ from __future__ import annotations
2
+
3
+ import builtins
4
+ from functools import cached_property
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from discogs_sdk._async._lazy import AsyncLazyResource
8
+ from discogs_sdk._async._paginator import AsyncPage
9
+ from discogs_sdk._async._resource import AsyncAPIResource
10
+ from discogs_sdk.models.collection import (
11
+ CollectionField,
12
+ CollectionFolder,
13
+ CollectionItem,
14
+ CollectionValue_,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from discogs_sdk._async._client import AsyncDiscogs
19
+
20
+
21
+ # --- Deep chaining: folder -> releases -> instances -> fields ---
22
+
23
+
24
+ class InstanceFields(AsyncAPIResource):
25
+ """Edit a custom field value on a specific instance."""
26
+
27
+ def __init__(self, client, username: str, folder_id: int, release_id: int, instance_id: int) -> None:
28
+ super().__init__(client)
29
+ self._username = username
30
+ self._folder_id = folder_id
31
+ self._release_id = release_id
32
+ self._instance_id = instance_id
33
+
34
+ async def update(self, field_id: int, *, value: str) -> None:
35
+ response = await self._post(
36
+ f"/users/{self._username}/collection/folders/{self._folder_id}"
37
+ f"/releases/{self._release_id}/instances/{self._instance_id}/fields/{field_id}",
38
+ json={"value": value},
39
+ )
40
+ self._raise_for_error(response)
41
+
42
+
43
+ class InstanceRef:
44
+ """Lightweight ref that captures instance_id for field editing."""
45
+
46
+ def __init__(self, client: AsyncDiscogs, username: str, folder_id: int, release_id: int, instance_id: int) -> None:
47
+ self._client = client
48
+ self._username = username
49
+ self._folder_id = folder_id
50
+ self._release_id = release_id
51
+ self._instance_id = instance_id
52
+
53
+ @cached_property
54
+ def fields(self) -> InstanceFields:
55
+ return InstanceFields(self._client, self._username, self._folder_id, self._release_id, self._instance_id)
56
+
57
+
58
+ class CollectionInstances(AsyncAPIResource):
59
+ """Manage instances of a release within a folder."""
60
+
61
+ def __init__(self, client, username: str, folder_id: int, release_id: int) -> None:
62
+ super().__init__(client)
63
+ self._username = username
64
+ self._folder_id = folder_id
65
+ self._release_id = release_id
66
+
67
+ def _base_path(self) -> str:
68
+ return f"/users/{self._username}/collection/folders/{self._folder_id}/releases/{self._release_id}/instances"
69
+
70
+ def get(self, instance_id: int) -> InstanceRef:
71
+ return InstanceRef(self._client, self._username, self._folder_id, self._release_id, instance_id)
72
+
73
+ async def delete(self, instance_id: int) -> None:
74
+ response = await self._delete(f"{self._base_path()}/{instance_id}")
75
+ self._raise_for_error(response)
76
+
77
+ async def update(self, instance_id: int, **kwargs: Any) -> None:
78
+ response = await self._post(f"{self._base_path()}/{instance_id}", json=kwargs)
79
+ self._raise_for_error(response)
80
+
81
+
82
+ class FolderReleaseRef:
83
+ """Lightweight ref for a release within a folder — provides .instances accessor."""
84
+
85
+ def __init__(self, client: AsyncDiscogs, username: str, folder_id: int, release_id: int) -> None:
86
+ self._client = client
87
+ self._username = username
88
+ self._folder_id = folder_id
89
+ self._release_id = release_id
90
+
91
+ @cached_property
92
+ def instances(self) -> CollectionInstances:
93
+ return CollectionInstances(self._client, self._username, self._folder_id, self._release_id)
94
+
95
+
96
+ class FolderReleases(AsyncAPIResource):
97
+ """Items in a collection folder."""
98
+
99
+ def __init__(self, client, username: str, folder_id: int) -> None:
100
+ super().__init__(client)
101
+ self._username = username
102
+ self._folder_id = folder_id
103
+
104
+ def _base_path(self) -> str:
105
+ return f"/users/{self._username}/collection/folders/{self._folder_id}/releases"
106
+
107
+ def get(self, release_id: int) -> FolderReleaseRef:
108
+ return FolderReleaseRef(self._client, self._username, self._folder_id, release_id)
109
+
110
+ def list(
111
+ self,
112
+ *,
113
+ sort: str | None = None,
114
+ sort_order: str | None = None,
115
+ ) -> AsyncPage[CollectionItem]:
116
+ params = {k: v for k, v in {"sort": sort, "sort_order": sort_order}.items() if v}
117
+ return AsyncPage(
118
+ client=self._client,
119
+ items_key="releases",
120
+ model_cls=CollectionItem,
121
+ params=params,
122
+ path=self._base_path(),
123
+ )
124
+
125
+ async def create(self, *, release_id: int) -> None:
126
+ response = await self._post(f"{self._base_path()}/{release_id}")
127
+ self._raise_for_error(response)
128
+
129
+
130
+ # --- Collection Folders ---
131
+
132
+
133
+ class CollectionFolders(AsyncAPIResource):
134
+ def __init__(self, client, username: str) -> None:
135
+ super().__init__(client)
136
+ self._username = username
137
+
138
+ def _base_path(self) -> str:
139
+ return f"/users/{self._username}/collection/folders"
140
+
141
+ def get(self, folder_id: int) -> AsyncLazyResource:
142
+ return AsyncLazyResource(
143
+ client=self._client,
144
+ path=f"{self._base_path()}/{folder_id}",
145
+ model_cls=CollectionFolder,
146
+ sub_resources={
147
+ "releases": lambda: FolderReleases(self._client, self._username, folder_id),
148
+ },
149
+ )
150
+
151
+ async def list(self) -> builtins.list[CollectionFolder]:
152
+ response = await self._get(self._base_path())
153
+ return self._parse_list_response(response, CollectionFolder, "folders")
154
+
155
+ async def create(self, *, name: str) -> CollectionFolder:
156
+ response = await self._post(self._base_path(), json={"name": name})
157
+ return self._parse_response(response, CollectionFolder)
158
+
159
+ async def delete(self, folder_id: int) -> None:
160
+ response = await self._delete(f"{self._base_path()}/{folder_id}")
161
+ self._raise_for_error(response)
162
+
163
+ async def update(self, folder_id: int, *, name: str) -> CollectionFolder:
164
+ response = await self._post(f"{self._base_path()}/{folder_id}", json={"name": name})
165
+ return self._parse_response(response, CollectionFolder)
166
+
167
+
168
+ # --- Collection Releases (cross-folder) ---
169
+
170
+
171
+ class CollectionReleaseRef:
172
+ """Ref for a release across all folders. Provides .list() for instances."""
173
+
174
+ def __init__(self, client: AsyncDiscogs, username: str, release_id: int) -> None:
175
+ self._client = client
176
+ self._username = username
177
+ self._release_id = release_id
178
+
179
+ def list(self) -> AsyncPage[CollectionItem]:
180
+ return AsyncPage(
181
+ client=self._client,
182
+ items_key="releases",
183
+ model_cls=CollectionItem,
184
+ path=f"/users/{self._username}/collection/releases/{self._release_id}",
185
+ )
186
+
187
+
188
+ class CollectionReleases(AsyncAPIResource):
189
+ def __init__(self, client, username: str) -> None:
190
+ super().__init__(client)
191
+ self._username = username
192
+
193
+ def get(self, release_id: int) -> CollectionReleaseRef:
194
+ return CollectionReleaseRef(self._client, self._username, release_id)
195
+
196
+
197
+ # --- Collection Fields ---
198
+
199
+
200
+ class CollectionFields(AsyncAPIResource):
201
+ def __init__(self, client, username: str) -> None:
202
+ super().__init__(client)
203
+ self._username = username
204
+
205
+ async def list(self) -> builtins.list[CollectionField]:
206
+ response = await self._get(f"/users/{self._username}/collection/fields")
207
+ return self._parse_list_response(response, CollectionField, "fields")
208
+
209
+
210
+ # --- Collection Value ---
211
+
212
+
213
+ class CollectionValueResource(AsyncAPIResource):
214
+ def __init__(self, client, username: str) -> None:
215
+ super().__init__(client)
216
+ self._username = username
217
+
218
+ def get(self) -> AsyncLazyResource:
219
+ return AsyncLazyResource(
220
+ client=self._client,
221
+ model_cls=CollectionValue_,
222
+ path=f"/users/{self._username}/collection/value",
223
+ )
224
+
225
+
226
+ # --- Collection namespace ---
227
+
228
+
229
+ class Collection:
230
+ def __init__(self, client: AsyncDiscogs, username: str) -> None:
231
+ self._client = client
232
+ self._username = username
233
+
234
+ @cached_property
235
+ def fields(self) -> CollectionFields:
236
+ return CollectionFields(self._client, self._username)
237
+
238
+ @cached_property
239
+ def folders(self) -> CollectionFolders:
240
+ return CollectionFolders(self._client, self._username)
241
+
242
+ @cached_property
243
+ def releases(self) -> CollectionReleases:
244
+ return CollectionReleases(self._client, self._username)
245
+
246
+ @cached_property
247
+ def value(self) -> CollectionValueResource:
248
+ return CollectionValueResource(self._client, self._username)
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from discogs_sdk._async._lazy import AsyncLazyResource
4
+ from discogs_sdk._async._paginator import AsyncPage
5
+ from discogs_sdk._async._resource import AsyncAPIResource
6
+ from discogs_sdk.models.export import Export
7
+
8
+
9
+ class Exports(AsyncAPIResource):
10
+ def get(self, export_id: int) -> AsyncLazyResource:
11
+ return AsyncLazyResource(
12
+ client=self._client,
13
+ model_cls=Export,
14
+ path=f"/inventory/export/{export_id}",
15
+ )
16
+
17
+ def list(self) -> AsyncPage[Export]:
18
+ return AsyncPage(
19
+ client=self._client,
20
+ items_key="items",
21
+ model_cls=Export,
22
+ path="/inventory/export",
23
+ )
24
+
25
+ async def download(self, export_id: int) -> bytes:
26
+ return await self._get_binary(f"/inventory/export/{export_id}/download")
27
+
28
+ async def request(self) -> None:
29
+ response = await self._post("/inventory/export")
30
+ self._raise_for_error(response)
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from discogs_sdk._async._lazy import AsyncLazyResource
4
+ from discogs_sdk._async._paginator import AsyncPage
5
+ from discogs_sdk._async._resource import AsyncAPIResource
6
+ from discogs_sdk.models.label import Label, LabelRelease
7
+
8
+
9
+ class LabelReleases(AsyncAPIResource):
10
+ def __init__(self, client, label_id: int) -> None:
11
+ super().__init__(client)
12
+ self._label_id = label_id
13
+
14
+ def list(self) -> AsyncPage[LabelRelease]:
15
+ return AsyncPage(
16
+ client=self._client,
17
+ items_key="releases",
18
+ model_cls=LabelRelease,
19
+ path=f"/labels/{self._label_id}/releases",
20
+ )
21
+
22
+
23
+ class Labels(AsyncAPIResource):
24
+ def get(self, label_id: int) -> AsyncLazyResource:
25
+ return AsyncLazyResource(
26
+ client=self._client,
27
+ model_cls=Label,
28
+ path=f"/labels/{label_id}",
29
+ sub_resources={
30
+ "releases": lambda: LabelReleases(self._client, label_id),
31
+ },
32
+ )
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from discogs_sdk._async._lazy import AsyncLazyResource
4
+ from discogs_sdk._async._paginator import AsyncPage
5
+ from discogs_sdk._async._resource import AsyncAPIResource
6
+ from discogs_sdk.models.list_ import List_, ListSummary
7
+
8
+
9
+ class UserLists(AsyncAPIResource):
10
+ """User's lists: GET /users/{username}/lists (paginated)."""
11
+
12
+ def __init__(self, client, username: str) -> None:
13
+ super().__init__(client)
14
+ self._username = username
15
+
16
+ def list(self) -> AsyncPage[ListSummary]:
17
+ return AsyncPage(
18
+ client=self._client,
19
+ items_key="lists",
20
+ model_cls=ListSummary,
21
+ path=f"/users/{self._username}/lists",
22
+ )
23
+
24
+
25
+ class Lists(AsyncAPIResource):
26
+ """Top-level list access: GET /lists/{id}."""
27
+
28
+ def get(self, list_id: int) -> AsyncLazyResource:
29
+ return AsyncLazyResource(
30
+ client=self._client,
31
+ model_cls=List_,
32
+ path=f"/lists/{list_id}",
33
+ )
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import cached_property
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ from discogs_sdk.models._common import Condition, CurrencyCode
7
+
8
+ from discogs_sdk._async._lazy import AsyncLazyResource
9
+ from discogs_sdk._async._paginator import AsyncPage
10
+ from discogs_sdk._async._resource import AsyncAPIResource
11
+ from discogs_sdk.models.marketplace import Fee, Listing, Order, OrderMessage
12
+
13
+ if TYPE_CHECKING:
14
+ from discogs_sdk._async._client import AsyncDiscogs
15
+
16
+
17
+ class OrderMessages(AsyncAPIResource):
18
+ def __init__(self, client, order_id: str) -> None:
19
+ super().__init__(client)
20
+ self._order_id = order_id
21
+
22
+ def list(self) -> AsyncPage[OrderMessage]:
23
+ return AsyncPage(
24
+ client=self._client,
25
+ items_key="messages",
26
+ model_cls=OrderMessage,
27
+ path=f"/marketplace/orders/{self._order_id}/messages",
28
+ )
29
+
30
+ async def create(self, *, message: str | None = None, status: str | None = None) -> OrderMessage:
31
+ body = {k: v for k, v in {"message": message, "status": status}.items() if v is not None}
32
+ response = await self._post(
33
+ f"/marketplace/orders/{self._order_id}/messages",
34
+ json=body,
35
+ )
36
+ return self._parse_response(response, OrderMessage)
37
+
38
+
39
+ class MarketplaceOrders(AsyncAPIResource):
40
+ def get(self, order_id: str) -> AsyncLazyResource:
41
+ return AsyncLazyResource(
42
+ client=self._client,
43
+ model_cls=Order,
44
+ path=f"/marketplace/orders/{order_id}",
45
+ sub_resources={
46
+ "messages": lambda: OrderMessages(self._client, order_id),
47
+ },
48
+ )
49
+
50
+ def list(
51
+ self,
52
+ *,
53
+ status: str | None = None,
54
+ sort: str | None = None,
55
+ sort_order: str | None = None,
56
+ ) -> AsyncPage[Order]:
57
+ params = {k: v for k, v in {"status": status, "sort": sort, "sort_order": sort_order}.items() if v}
58
+ return AsyncPage(
59
+ client=self._client,
60
+ items_key="orders",
61
+ model_cls=Order,
62
+ params=params,
63
+ path="/marketplace/orders",
64
+ )
65
+
66
+ async def update(self, order_id: str, **kwargs: Any) -> Order:
67
+ response = await self._post(f"/marketplace/orders/{order_id}", json=kwargs)
68
+ return self._parse_response(response, Order)
69
+
70
+
71
+ class MarketplaceListings(AsyncAPIResource):
72
+ def get(self, listing_id: int) -> AsyncLazyResource:
73
+ return AsyncLazyResource(
74
+ client=self._client,
75
+ model_cls=Listing,
76
+ path=f"/marketplace/listings/{listing_id}",
77
+ )
78
+
79
+ async def create(
80
+ self, *, release_id: int, condition: Condition, price: float, status: str = "For Sale", **kwargs: Any
81
+ ) -> Listing:
82
+ body = {"release_id": release_id, "condition": condition, "price": price, "status": status, **kwargs}
83
+ response = await self._post("/marketplace/listings", json=body)
84
+ return self._parse_response(response, Listing)
85
+
86
+ async def update(self, listing_id: int, **kwargs: Any) -> None:
87
+ response = await self._post(f"/marketplace/listings/{listing_id}", json=kwargs)
88
+ self._raise_for_error(response)
89
+
90
+ async def delete(self, listing_id: int) -> None:
91
+ response = await self._delete(f"/marketplace/listings/{listing_id}")
92
+ self._raise_for_error(response)
93
+
94
+
95
+ class MarketplaceFee(AsyncAPIResource):
96
+ def get(self, *, price: float, currency: CurrencyCode | None = None) -> AsyncLazyResource:
97
+ if currency:
98
+ path = f"/marketplace/fee/{price}/{currency}"
99
+ else:
100
+ path = f"/marketplace/fee/{price}"
101
+ return AsyncLazyResource(
102
+ client=self._client,
103
+ model_cls=Fee,
104
+ path=path,
105
+ )
106
+
107
+
108
+ class Marketplace:
109
+ def __init__(self, client: AsyncDiscogs) -> None:
110
+ self._client = client
111
+
112
+ @cached_property
113
+ def fee(self) -> MarketplaceFee:
114
+ return MarketplaceFee(self._client)
115
+
116
+ @cached_property
117
+ def listings(self) -> MarketplaceListings:
118
+ return MarketplaceListings(self._client)
119
+
120
+ @cached_property
121
+ def orders(self) -> MarketplaceOrders:
122
+ return MarketplaceOrders(self._client)