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.
- discogs_sdk/__init__.py +134 -0
- discogs_sdk/_async/__init__.py +3 -0
- discogs_sdk/_async/_client.py +258 -0
- discogs_sdk/_async/_lazy.py +94 -0
- discogs_sdk/_async/_oauth.py +100 -0
- discogs_sdk/_async/_paginator.py +100 -0
- discogs_sdk/_async/_resource.py +92 -0
- discogs_sdk/_async/resources/__init__.py +75 -0
- discogs_sdk/_async/resources/artists.py +39 -0
- discogs_sdk/_async/resources/collection.py +248 -0
- discogs_sdk/_async/resources/exports.py +30 -0
- discogs_sdk/_async/resources/labels.py +32 -0
- discogs_sdk/_async/resources/lists.py +33 -0
- discogs_sdk/_async/resources/marketplace.py +122 -0
- discogs_sdk/_async/resources/masters.py +50 -0
- discogs_sdk/_async/resources/releases.py +96 -0
- discogs_sdk/_async/resources/search.py +22 -0
- discogs_sdk/_async/resources/uploads.py +35 -0
- discogs_sdk/_async/resources/users.py +168 -0
- discogs_sdk/_async/resources/wantlist.py +34 -0
- discogs_sdk/_base_client.py +177 -0
- discogs_sdk/_exceptions.py +60 -0
- discogs_sdk/_sync/__init__.py +6 -0
- discogs_sdk/_sync/_client.py +230 -0
- discogs_sdk/_sync/_lazy.py +72 -0
- discogs_sdk/_sync/_oauth.py +85 -0
- discogs_sdk/_sync/_paginator.py +89 -0
- discogs_sdk/_sync/_resource.py +66 -0
- discogs_sdk/_sync/resources/__init__.py +78 -0
- discogs_sdk/_sync/resources/artists.py +34 -0
- discogs_sdk/_sync/resources/collection.py +228 -0
- discogs_sdk/_sync/resources/exports.py +23 -0
- discogs_sdk/_sync/resources/labels.py +29 -0
- discogs_sdk/_sync/resources/lists.py +28 -0
- discogs_sdk/_sync/resources/marketplace.py +101 -0
- discogs_sdk/_sync/resources/masters.py +43 -0
- discogs_sdk/_sync/resources/releases.py +81 -0
- discogs_sdk/_sync/resources/search.py +19 -0
- discogs_sdk/_sync/resources/uploads.py +28 -0
- discogs_sdk/_sync/resources/users.py +160 -0
- discogs_sdk/_sync/resources/wantlist.py +30 -0
- discogs_sdk/models/__init__.py +109 -0
- discogs_sdk/models/_common.py +171 -0
- discogs_sdk/models/artist.py +34 -0
- discogs_sdk/models/collection.py +38 -0
- discogs_sdk/models/export.py +15 -0
- discogs_sdk/models/label.py +31 -0
- discogs_sdk/models/list_.py +38 -0
- discogs_sdk/models/marketplace.py +90 -0
- discogs_sdk/models/master.py +40 -0
- discogs_sdk/models/release.py +94 -0
- discogs_sdk/models/search.py +28 -0
- discogs_sdk/models/upload.py +17 -0
- discogs_sdk/models/user.py +46 -0
- discogs_sdk/models/wantlist.py +11 -0
- discogs_sdk/oauth.py +21 -0
- discogs_sdk/py.typed +0 -0
- discogs_sdk-0.1.0.dist-info/METADATA +339 -0
- discogs_sdk-0.1.0.dist-info/RECORD +60 -0
- 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)
|