Skip to content

Commit 8a1579c

Browse files
committed
feat: update for new file system implementation
1 parent d72102b commit 8a1579c

File tree

5 files changed

+142
-162
lines changed

5 files changed

+142
-162
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to `audiostack` will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [3.0.0] - 2025-04-23
8+
- Implementation of new file system
9+
- Folders are now represented by ids and will have parents of their own
10+
- File Item and Folder Item have new values with more information
11+
- Categories are represented by ids, they can be found by name
12+
713
## [2.10.1] - 2025-02-24
814
- Fixed logic that removed 0 float values from payload
915

audiostack/content/file.py

Lines changed: 123 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import time
3-
from typing import Any
3+
from typing import Any, Optional
4+
from uuid import UUID
45

56
from audiostack import TIMEOUT_THRESHOLD_S
67
from audiostack.helpers.api_item import APIResponseItem
@@ -16,16 +17,22 @@ class File:
1617
class Item(APIResponseItem):
1718
def __init__(self, response: dict) -> None:
1819
super().__init__(response)
19-
self.fileId = self.data["fileId"]
20-
self.filePath = self.data["filePath"]
21-
self.url = self.data.get("url", None)
22-
23-
def delete(self) -> APIResponseItem:
24-
return File.delete(self.fileId)
25-
26-
def download(self, fileName: str = "", path: str = "./") -> None:
20+
self.file_id = response["fileId"]
21+
self.file_name = response["fileName"]
22+
self.url = response.get("url", None)
23+
self.created_by = response["createdBy"]
24+
self.last_modified = response.get("lastModified", None)
25+
self.file_type_id = response["fileTypeId"]
26+
self.category_id = response.get("categoryId", None)
27+
self.size = response["size"]
28+
self.created_at = response["createdAt"]
29+
self.status = response["status"]
30+
31+
def download(self, fileName: str, path: str = "./") -> None:
2732
if not fileName:
28-
fileName = self.filePath.split("/")[-1]
33+
raise Exception("Please supply a valid file name")
34+
if not self.url:
35+
raise Exception("No URL found for this file")
2936
RequestInterface.download_url(self.url, destination=path, name=fileName)
3037

3138
class List(APIResponseList):
@@ -40,136 +47,100 @@ def resolve_item(self, list_type: str, item: Any) -> "File.Item":
4047

4148
@staticmethod
4249
def create(
43-
localPath: str,
44-
uploadPath: str,
45-
fileType: str,
50+
local_path: str,
51+
file_name: str,
52+
folder_id: Optional[UUID] = None,
4653
category: str = "",
47-
tags: list = [],
48-
metadata: dict = {},
4954
) -> Item:
50-
if not os.path.isfile(localPath):
55+
if not local_path:
56+
raise Exception("Please supply a localPath (path to your local file)")
57+
58+
if not os.path.isfile(local_path):
5159
raise Exception("Supplied file does not eixst")
5260

53-
if not uploadPath or not localPath:
54-
raise Exception(
55-
"Please supply a localPath (path to your local file) and an uploadPath (path to where youd like this to be saved)"
56-
)
57-
58-
data = {
59-
"filePath": uploadPath,
60-
"fileType": fileType,
61-
"source": "pythonSDK",
62-
"category": category,
63-
"tags": tags,
64-
"metadata": metadata,
65-
}
61+
if not file_name:
62+
raise Exception("Please supply a valid file name")
6663

67-
r = File.interface.send_request(
68-
rtype=RequestTypes.POST,
69-
route="file/create-upload-url",
70-
json=data,
71-
)
72-
response = APIResponseItem(r)
73-
url = response.data["fileUploadUrl"]
74-
fileId = response.data["fileId"]
64+
if not folder_id:
65+
Folder.get_root().data["folders"]["folderId"]
7566

76-
File.interface.send_upload_request(local_path=localPath, upload_url=url)
77-
return File.get(fileId)
67+
category_id = File.get_category_id_by_name(category)
7868

79-
@staticmethod
80-
def transfer(
81-
url: str,
82-
uploadPath: str,
83-
category: str = "",
84-
tags: list = [],
85-
metadata: dict = {},
86-
) -> Item:
87-
data = {
88-
"filePath": uploadPath,
89-
"url": url,
90-
"category": category,
91-
"tags": tags,
92-
"metadata": metadata,
69+
payload = {
70+
"fileName": file_name,
71+
"folderId": folder_id,
72+
"categoryId": category_id,
9373
}
9474

9575
r = File.interface.send_request(
96-
rtype=RequestTypes.PUT,
97-
route="file/transfer-file",
98-
json=data,
76+
rtype=RequestTypes.POST,
77+
route="file/create-upload-url",
78+
json=payload,
79+
overwrite_base_url="https://v2.api.audio/v3",
9980
)
10081
response = APIResponseItem(r)
101-
return File.get(response.data["fileId"])
82+
url = response.data["uploadUrl"]
83+
84+
File.interface.send_upload_request(local_path=local_path, upload_url=url)
85+
return File.Item(r)
10286

10387
@staticmethod
10488
def modify(
105-
fileId: str,
106-
filePath: str = "",
89+
file_id: str,
90+
file_name: str,
10791
category: str = "",
108-
tags: list = [],
109-
metadata: dict = {},
11092
) -> Item:
111-
data = {
112-
"filePath": filePath,
113-
"category": category,
114-
"tags": tags,
115-
"metadata": metadata,
93+
category_id = File.get_category_id_by_name(category)
94+
payload = {
95+
"fileName": file_name,
96+
"categoryId": category_id,
11697
}
117-
118-
File.interface.send_request(
98+
r = File.interface.send_request(
11999
rtype=RequestTypes.PATCH,
120-
route=f"file/id/{fileId}",
121-
json=data,
100+
route=f"file/{file_id}",
101+
json=payload,
102+
overwrite_base_url="https://v2.api.audio/v3",
122103
)
123-
return File.get(fileId)
104+
return File.Item(r)
124105

125106
@staticmethod
126-
def get(fileId: str) -> Item:
107+
def get(file_id: str) -> Item:
127108
r = File.interface.send_request(
128-
rtype=RequestTypes.GET, route="file/id", path_parameters=fileId
109+
rtype=RequestTypes.GET,
110+
route=f"file/{file_id}",
111+
overwrite_base_url="https://v2.api.audio/v3",
129112
)
130-
start = time.time()
131-
132-
while r["statusCode"] == 202:
133-
print("Response in progress please wait...")
134-
r = File.interface.send_request(
135-
rtype=RequestTypes.GET, route="file/id", path_parameters=fileId
136-
)
137-
if time.time() - start >= TIMEOUT_THRESHOLD_S:
138-
raise TimeoutError(
139-
f"Polling File timed out after 5 minutes. Please contact us for support. FileId: {fileId}"
140-
)
141113
return File.Item(r)
142114

143115
@staticmethod
144-
def delete(fileId: str) -> APIResponseItem:
116+
def delete(file_id: str, folder_id: str) -> APIResponseItem:
117+
# Needs more thought
145118
r = File.interface.send_request(
146-
rtype=RequestTypes.DELETE, route="file/id", path_parameters=fileId
119+
rtype=RequestTypes.DELETE,
120+
route=f"file/{file_id}/{folder_id}",
121+
overwrite_base_url="https://v2.api.audio/v3",
147122
)
148123
return APIResponseItem(r)
149124

150125
@staticmethod
151-
def search(
152-
path: str = "",
153-
source: str = "",
154-
tags: list = [],
155-
name: str = "",
156-
fileType: str = "",
157-
category: str = "",
158-
sortBy: str = "",
159-
) -> List:
160-
queries = {
161-
"path": path,
162-
"source": source,
163-
"tags": tags,
164-
"name": name,
165-
"fileType": fileType,
166-
"category": category,
167-
"soryBy": sortBy,
168-
}
126+
def get_file_categories() -> APIResponseItem:
169127
r = File.interface.send_request(
170-
rtype=RequestTypes.GET, route="file/search", query_parameters=queries
128+
rtype=RequestTypes.GET,
129+
route="file/metadata/file-categories",
130+
overwrite_base_url="https://v2.api.audio/v3",
171131
)
172-
return File.List(r, list_type="items")
132+
return APIResponseItem(r)
133+
134+
@staticmethod
135+
def get_category_id_by_name(name: str) -> Optional[UUID]:
136+
category_id = None
137+
categories = [
138+
{"category_id": y["categoryId"], "name": y["name"]}
139+
for x in File.get_file_categories().data["fileTypes"]
140+
for y in x["fileCategories"]
141+
]
142+
category_id = next(filter(lambda x: x["name"] == name, categories), None)
143+
return category_id
173144

174145

175146
class Folder:
@@ -179,8 +150,9 @@ class Folder:
179150
class Item(APIResponseItem):
180151
def __init__(self, response: dict) -> None:
181152
super().__init__(response)
182-
self.folders = Folder.List(response, list_type="folders")
183-
self.files = File.List(response, list_type="files")
153+
self.folders = Folder.List(response["folders"], list_type="folders")
154+
self.files = File.List(response["files"], list_type="files")
155+
self.current_path_chain = response["currentPathChain"]
184156

185157
class List(APIResponseList):
186158
def __init__(self, response: dict, list_type: str) -> None:
@@ -193,26 +165,58 @@ def resolve_item(self, list_type: str, item: Any) -> dict:
193165
raise Exception()
194166

195167
@staticmethod
196-
def create(name: Any) -> APIResponseItem:
197-
r = File.interface.send_request(
168+
def get_root() -> Item:
169+
r = Folder.interface.send_request(
170+
rtype=RequestTypes.GET,
171+
route="v3/file/folder",
172+
overwrite_base_url="https://v2.api.audio",
173+
)
174+
return Folder.Item(r)
175+
176+
@staticmethod
177+
def create(name: str, parent_folder_id: Optional[UUID] = None) -> APIResponseItem:
178+
folder = {
179+
"folderName": name,
180+
"parentFolderId": parent_folder_id,
181+
}
182+
r = Folder.interface.send_request(
198183
rtype=RequestTypes.POST,
199-
route="folder",
200-
json={"folder": name},
184+
route="file/folder",
185+
json=folder,
186+
overwrite_base_url="https://v2.api.audio/v3",
201187
)
202188
return APIResponseItem(r)
203189

204190
@staticmethod
205-
def list(folder: str) -> "Folder.Item":
206-
r = File.interface.send_request(
207-
rtype=RequestTypes.GET, route="folder", query_parameters={"folder": folder}
191+
def get(folder_id: UUID) -> APIResponseItem:
192+
r = Folder.interface.send_request(
193+
rtype=RequestTypes.GET,
194+
route=f"folder/{folder_id}",
195+
overwrite_base_url="https://v2.api.audio/v3",
208196
)
209-
return Folder.Item(r)
197+
return APIResponseItem(r)
198+
199+
@staticmethod
200+
def modify(
201+
folder_id: UUID,
202+
name: str,
203+
) -> APIResponseItem:
204+
folder = {
205+
"folderName": name,
206+
}
207+
r = Folder.interface.send_request(
208+
rtype=RequestTypes.PATCH,
209+
route=f"folder/{folder_id}",
210+
json=folder,
211+
overwrite_base_url="https://v2.api.audio/v3",
212+
)
213+
return APIResponseItem(r)
210214

211215
@staticmethod
212-
def delete(folder: str, delete_files: bool = False) -> APIResponseItem:
216+
def delete(folder_id: UUID) -> APIResponseItem:
213217
r = File.interface.send_request(
214218
rtype=RequestTypes.DELETE,
215-
route="folder",
216-
query_parameters={"folder": folder, "forceDelete": delete_files},
219+
route=f"folder/{folder_id}",
220+
overwrite_base_url="https://v2.api.audio/v3",
217221
)
218222
return APIResponseItem(r)

audiostack/tests/content/test_file.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44

55
import audiostack
6-
from audiostack.content.file import File
6+
from audiostack.content.file import File, Folder
77

88
audiostack.api_base = os.environ.get("AUDIO_STACK_DEV_URL", "https://v2.api.audio")
99
audiostack.api_key = os.environ["AUDIO_STACK_DEV_KEY"] # type: ignore
@@ -12,8 +12,9 @@
1212

1313

1414
def test_create() -> None:
15-
r = File.create(localPath="example.mp3", uploadPath="example.mp3", fileType="audio")
16-
test_constants["fileId"] = r.fileId
15+
r = File.create(local_path="example.mp3", file_name="test")
16+
test_constants["fileId"] = r.file_id
17+
test_constants["fileName"] = r.file_name
1718
print(r)
1819

1920

@@ -23,36 +24,14 @@ def test_get() -> None:
2324

2425

2526
def test_modify() -> None:
26-
r = File.modify(fileId=test_constants["fileId"], category="test")
27+
r = File.modify(
28+
file_id=test_constants["fileId"], file_name=test_constants["fileName"]
29+
)
2730
print(r)
2831

2932

30-
@pytest.mark.skip(reason="Raises KeyError: 'items'")
31-
def test_search() -> None:
32-
files = File.search()
33-
for f in files:
34-
print(f)
35-
36-
files = File.search(source="pythonSDK")
37-
for f in files:
38-
print(f)
39-
40-
4133
def test_delete() -> None:
42-
r = File.get(test_constants["fileId"])
43-
r2 = r.delete()
44-
print(r2)
45-
46-
47-
def test_create_2() -> None:
48-
r = File.create(
49-
localPath="example.mp3",
50-
uploadPath="example.mp3",
51-
fileType="audio",
52-
category="sounds",
53-
tags=["a", "b"],
54-
metadata={"hello": "world"},
34+
r = File.delete(
35+
test_constants["fileId"], Folder.get_root().current_path_chain["folderId"]
5536
)
56-
test_constants["fileId"] = r.fileId
5737
print(r)
58-
r.delete()

0 commit comments

Comments
 (0)