dadascribe 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.
- dadascribe/__init__.py +2 -0
- dadascribe/cli.py +127 -0
- dadascribe/internal_globals.py +4 -0
- dadascribe/request_utils.py +173 -0
- dadascribe/wrapper.py +116 -0
- dadascribe-0.1.0.dist-info/METADATA +74 -0
- dadascribe-0.1.0.dist-info/RECORD +10 -0
- dadascribe-0.1.0.dist-info/WHEEL +4 -0
- dadascribe-0.1.0.dist-info/entry_points.txt +2 -0
- dadascribe-0.1.0.dist-info/licenses/LICENSE +21 -0
dadascribe/__init__.py
ADDED
dadascribe/cli.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
|
|
2
|
+
import argparse
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from .internal_globals import ENV_API_NAME
|
|
8
|
+
from .wrapper import ScribeAPIWrapper, DownloadError
|
|
9
|
+
from .request_utils import InternalRequestError, InvalidFileError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _check_api_key_presence(args) -> str:
|
|
13
|
+
api_key = args.api_key or os.getenv(ENV_API_NAME)
|
|
14
|
+
if not api_key:
|
|
15
|
+
print(
|
|
16
|
+
"Error: API key required. Provide --api-key " \
|
|
17
|
+
"or set DADASCRIBE_API_KEY env var.",
|
|
18
|
+
file=sys.stderr,
|
|
19
|
+
)
|
|
20
|
+
sys.exit(2)
|
|
21
|
+
|
|
22
|
+
return api_key
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_args() -> argparse.Namespace:
|
|
26
|
+
p = argparse.ArgumentParser(
|
|
27
|
+
description="Interact with the DaDaScribe API through the CLI."
|
|
28
|
+
)
|
|
29
|
+
p.add_argument(
|
|
30
|
+
"--api-key",
|
|
31
|
+
help="Dadascribe API key (or set DADASCRIBE_API_KEY env var).",
|
|
32
|
+
)
|
|
33
|
+
p.add_argument(
|
|
34
|
+
"--source",
|
|
35
|
+
default="https://www.youtube.com/watch?v=VIDEO_ID",
|
|
36
|
+
help="Source media URL (e.g. YouTube link) OR file path.",
|
|
37
|
+
)
|
|
38
|
+
p.add_argument(
|
|
39
|
+
"--source-language", default="en", help="Source language (e.g. 'en')."
|
|
40
|
+
)
|
|
41
|
+
p.add_argument(
|
|
42
|
+
"--destination-language",
|
|
43
|
+
default="",
|
|
44
|
+
help="Comma-separated destination languages (e.g. 'it,fr').",
|
|
45
|
+
)
|
|
46
|
+
p.add_argument(
|
|
47
|
+
"--diarization",
|
|
48
|
+
default="",
|
|
49
|
+
help="Comma-separated diarization labels (e.g. 'Alice,Bob').",
|
|
50
|
+
)
|
|
51
|
+
p.add_argument(
|
|
52
|
+
"--timeout", type=int, default=60, help="Request timeout in seconds."
|
|
53
|
+
)
|
|
54
|
+
p.add_argument(
|
|
55
|
+
"--dump-response",
|
|
56
|
+
help="File to save JSON response (optional). "
|
|
57
|
+
"If omitted, prints to stdout.",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
p.add_argument(
|
|
61
|
+
"--status-id",
|
|
62
|
+
help="Check status for a previously submitted job ID (e.g. " \
|
|
63
|
+
"a1B2c3D4e5F6g7H8). If provided, transcribe will not run.",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# For downloading transcription files:
|
|
67
|
+
|
|
68
|
+
p.add_argument(
|
|
69
|
+
"--download",
|
|
70
|
+
help="Download generated files given a job ID " \
|
|
71
|
+
"& an optional output dir path" \
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
p.add_argument(
|
|
75
|
+
"--download-output-dir",
|
|
76
|
+
default=None,
|
|
77
|
+
help="Directory to save downloaded files (optional). " \
|
|
78
|
+
"If omitted, saves to current directory."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return p.parse_args()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _handle_args(args: argparse.Namespace, api_key: str) -> None:
|
|
85
|
+
wrapper = ScribeAPIWrapper(api_key=api_key, req_timeout=args.timeout)
|
|
86
|
+
try:
|
|
87
|
+
if args.status_id:
|
|
88
|
+
# If a status ID is provided, perform a status
|
|
89
|
+
# check and ignore other transcribe options.
|
|
90
|
+
result = wrapper.retrieve_status(id=args.status_id)
|
|
91
|
+
elif args.download:
|
|
92
|
+
result = wrapper.download_transcription_output(
|
|
93
|
+
id=args.download,
|
|
94
|
+
output_dir=args.download_output_dir or os.getcwd(),
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
result = wrapper.transcribe(
|
|
98
|
+
source=args.source,
|
|
99
|
+
source_language=args.source_language,
|
|
100
|
+
destination_language=args.destination_language,
|
|
101
|
+
diarization=args.diarization,
|
|
102
|
+
)
|
|
103
|
+
except (InternalRequestError, InvalidFileError, DownloadError) as e:
|
|
104
|
+
if isinstance(e, DownloadError) or isinstance(e, InvalidFileError):
|
|
105
|
+
print(f"Error: {e.message}", file=sys.stderr)
|
|
106
|
+
elif isinstance(e, InternalRequestError):
|
|
107
|
+
print(f"Error during request: {e.resp_body}", file=sys.stderr)
|
|
108
|
+
|
|
109
|
+
sys.exit(1)
|
|
110
|
+
|
|
111
|
+
if args.dump_response:
|
|
112
|
+
with open(args.dump_response, "w", encoding="utf-8") as f:
|
|
113
|
+
json.dump(result, f, ensure_ascii=False, indent=2)
|
|
114
|
+
print(f"Saved response to {args.dump_response}")
|
|
115
|
+
else:
|
|
116
|
+
# Pretty-print JSON or text
|
|
117
|
+
if isinstance(result, (dict, list)):
|
|
118
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
119
|
+
elif result is not None:
|
|
120
|
+
print(result)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def main() -> None:
|
|
125
|
+
parsed_args = parse_args()
|
|
126
|
+
api_key = _check_api_key_presence(parsed_args)
|
|
127
|
+
_handle_args(parsed_args, api_key)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Stores global constants & utils related to requests.
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from io import BufferedReader
|
|
4
|
+
from typing import Optional, Any
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from os import PathLike
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
BASE_API_URL: str = "https://api.dadascribe.com/v1/"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EndPoints(StrEnum):
|
|
16
|
+
"""Common endpoint names for the API."""
|
|
17
|
+
|
|
18
|
+
STATUS = "status"
|
|
19
|
+
TRANSCRIBE = "transcribe"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PayLoadKeys(StrEnum):
|
|
23
|
+
"""Common payload keys for the API."""
|
|
24
|
+
|
|
25
|
+
ID = "id"
|
|
26
|
+
SOURCE = "source"
|
|
27
|
+
SOURCE_LANGUAGE = "source-language"
|
|
28
|
+
DEST_LANGUAGE = "destination-language"
|
|
29
|
+
DIARIZATION = "diarization"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ResponseKeys(StrEnum):
|
|
33
|
+
"""Common response keys for the API."""
|
|
34
|
+
|
|
35
|
+
STATUS = "status"
|
|
36
|
+
URLS = "urls"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Status(StrEnum):
|
|
40
|
+
"""Common status values for the API."""
|
|
41
|
+
|
|
42
|
+
COMPLETE = "complete"
|
|
43
|
+
PROCESSING = "processing"
|
|
44
|
+
ERROR = "error"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def extract_response_content(resp: requests.Response) -> str:
|
|
48
|
+
"""Helper to extract the response content as JSON or raw text."""
|
|
49
|
+
try:
|
|
50
|
+
return resp.json()
|
|
51
|
+
except ValueError:
|
|
52
|
+
return resp.text
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class InternalRequestError(Exception):
|
|
56
|
+
"""Raised when a request fails with a non-200 status code.
|
|
57
|
+
Contains the response object for debugging and other useful
|
|
58
|
+
information about the failure."""
|
|
59
|
+
|
|
60
|
+
def __init__(self, response: requests.Response):
|
|
61
|
+
self._response = response
|
|
62
|
+
self._message = (
|
|
63
|
+
f"Request failed: {response.status_code} {response.reason}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
super().__init__(self.message)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def response(self) -> requests.Response:
|
|
70
|
+
return self._response
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def message(self) -> str:
|
|
74
|
+
return self._message
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def resp_body(self) -> str:
|
|
78
|
+
return extract_response_content(self._response)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class InvalidFileError(Exception):
|
|
82
|
+
"""Raised when an inputted file is not valid."""
|
|
83
|
+
|
|
84
|
+
def __init__(self, message: str):
|
|
85
|
+
self._message = message
|
|
86
|
+
super().__init__(self._message)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def message(self) -> str:
|
|
90
|
+
return self._message
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class RequestUtils:
|
|
94
|
+
"""Utility class for making HTTP requests,
|
|
95
|
+
handling common request logic."""
|
|
96
|
+
|
|
97
|
+
def exec_request(
|
|
98
|
+
self,
|
|
99
|
+
api_key: str,
|
|
100
|
+
url: str,
|
|
101
|
+
payload: Optional[dict],
|
|
102
|
+
timeout: int = 0,
|
|
103
|
+
get: bool = False,
|
|
104
|
+
file: Optional[PathLike] | Optional[str] = None
|
|
105
|
+
) -> str | None:
|
|
106
|
+
"""Executes a POST/GET request to the given URL with the given headers
|
|
107
|
+
and payload, and returns the response as JSON or raw text."""
|
|
108
|
+
req_fn = requests.get if get else requests.post
|
|
109
|
+
if file is not None:
|
|
110
|
+
headers = self.construct_headers(api_key, include_content=False)
|
|
111
|
+
payload, file_obj = self.construct_file_payload(file, payload)
|
|
112
|
+
resp = req_fn(
|
|
113
|
+
url,
|
|
114
|
+
headers=headers,
|
|
115
|
+
files=payload, timeout=timeout
|
|
116
|
+
)
|
|
117
|
+
file_obj.close()
|
|
118
|
+
else:
|
|
119
|
+
headers = self.construct_headers(api_key)
|
|
120
|
+
resp = req_fn(
|
|
121
|
+
url,
|
|
122
|
+
headers=headers,
|
|
123
|
+
json=payload, timeout=timeout
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
resp.raise_for_status()
|
|
128
|
+
except requests.HTTPError:
|
|
129
|
+
raise InternalRequestError(resp)
|
|
130
|
+
|
|
131
|
+
# Return JSON if possible, otherwise raw text
|
|
132
|
+
return extract_response_content(resp)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def construct_headers(
|
|
136
|
+
self,
|
|
137
|
+
api_key: str,
|
|
138
|
+
include_content: bool = True
|
|
139
|
+
) -> dict:
|
|
140
|
+
"""Contructs a commonly used headers dict with the given API key."""
|
|
141
|
+
headers = {
|
|
142
|
+
"Authorization": f"Bearer {api_key}",
|
|
143
|
+
}
|
|
144
|
+
if include_content:
|
|
145
|
+
headers["Content-Type"] = "application/json"
|
|
146
|
+
return headers
|
|
147
|
+
|
|
148
|
+
def is_link(self, object: Any) -> bool:
|
|
149
|
+
"""Returns whether a given string is a link or not."""
|
|
150
|
+
try:
|
|
151
|
+
stringed = str(object)
|
|
152
|
+
result = urlparse(stringed)
|
|
153
|
+
return all([result.scheme, result.netloc])
|
|
154
|
+
except ValueError:
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def construct_file_payload(
|
|
159
|
+
self,
|
|
160
|
+
file: PathLike | str,
|
|
161
|
+
params: Optional[dict] = None
|
|
162
|
+
) -> tuple[dict, BufferedReader]:
|
|
163
|
+
"""Constructs a file payload dict from the given file path."""
|
|
164
|
+
file = Path(file)
|
|
165
|
+
if not file.is_file():
|
|
166
|
+
raise InvalidFileError(f"File not found or is not a file: {file}")
|
|
167
|
+
|
|
168
|
+
file_obj = open(file, "rb")
|
|
169
|
+
file_payload = {
|
|
170
|
+
"file": (file.name, file_obj),
|
|
171
|
+
"data": (None, json.dumps(params or {}), "application/json"),
|
|
172
|
+
}
|
|
173
|
+
return file_payload, file_obj
|
dadascribe/wrapper.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from os import PathLike
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from .request_utils import (
|
|
6
|
+
BASE_API_URL,
|
|
7
|
+
EndPoints,
|
|
8
|
+
PayLoadKeys,
|
|
9
|
+
RequestUtils,
|
|
10
|
+
ResponseKeys,
|
|
11
|
+
Status,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DownloadError(Exception):
|
|
16
|
+
"""Raised when download fails."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, message: str):
|
|
19
|
+
self.message = message
|
|
20
|
+
super().__init__(self.message)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ScribeAPIWrapper:
|
|
24
|
+
def __init__(self, api_key: str, req_timeout: int = 60):
|
|
25
|
+
self._request_utils = RequestUtils()
|
|
26
|
+
self._api_key = api_key
|
|
27
|
+
self._req_timeout = req_timeout
|
|
28
|
+
|
|
29
|
+
def transcribe(
|
|
30
|
+
self,
|
|
31
|
+
source: str | list[str] | PathLike,
|
|
32
|
+
source_language: str,
|
|
33
|
+
destination_language: str,
|
|
34
|
+
diarization: Optional[str] = None,
|
|
35
|
+
) -> Any:
|
|
36
|
+
"""Send a POST request to the Dadascribe /v1/transcribe
|
|
37
|
+
endpoint and return the parsed response.
|
|
38
|
+
|
|
39
|
+
Raises InvalidRequestError on failing requests.
|
|
40
|
+
"""
|
|
41
|
+
url = BASE_API_URL + EndPoints.TRANSCRIBE
|
|
42
|
+
payload: dict[str, Any] = {
|
|
43
|
+
PayLoadKeys.SOURCE_LANGUAGE: source_language,
|
|
44
|
+
PayLoadKeys.DEST_LANGUAGE: destination_language,
|
|
45
|
+
}
|
|
46
|
+
if diarization is not None:
|
|
47
|
+
payload[PayLoadKeys.DIARIZATION] = diarization
|
|
48
|
+
file_source = None
|
|
49
|
+
if isinstance(source, list) or self._request_utils.is_link(source):
|
|
50
|
+
print("Source detected as LINK or LIST of LINKS.")
|
|
51
|
+
payload[PayLoadKeys.SOURCE] = source
|
|
52
|
+
else:
|
|
53
|
+
print("Source detected as FILE.")
|
|
54
|
+
# Construct files data:
|
|
55
|
+
file_source = source
|
|
56
|
+
|
|
57
|
+
return self._api_request(url, payload=payload, file=file_source)
|
|
58
|
+
|
|
59
|
+
def retrieve_status(self, id: str) -> Any:
|
|
60
|
+
"""Send a POST request to the Dadascribe /v1/status endpoint
|
|
61
|
+
and return the parsed response.
|
|
62
|
+
|
|
63
|
+
Raises InvalidRequestError on failing requests.
|
|
64
|
+
"""
|
|
65
|
+
url = BASE_API_URL + EndPoints.STATUS
|
|
66
|
+
payload = {PayLoadKeys.ID: id}
|
|
67
|
+
|
|
68
|
+
return self._api_request(url, payload=payload)
|
|
69
|
+
|
|
70
|
+
def download_transcription_output(
|
|
71
|
+
self,
|
|
72
|
+
id: str,
|
|
73
|
+
output_dir: str | PathLike,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Download the transcription output for the given job ID
|
|
76
|
+
to the specified directory. If no directory is specified,
|
|
77
|
+
saves to the current directory.
|
|
78
|
+
"""
|
|
79
|
+
if not os.path.exists(output_dir):
|
|
80
|
+
raise DownloadError(
|
|
81
|
+
f'Output directory "{output_dir}" does not exist.'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
status_info = self.retrieve_status(id)
|
|
85
|
+
if status_info[ResponseKeys.STATUS] != Status.COMPLETE:
|
|
86
|
+
raise DownloadError(
|
|
87
|
+
"Job is not completed yet. Cannot download output."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
for file_url in status_info.get(ResponseKeys.URLS, []):
|
|
91
|
+
file_name = os.path.basename(file_url)
|
|
92
|
+
file_content = self._api_request(file_url, get=True)
|
|
93
|
+
with open(os.path.join(output_dir, file_name), "w") as f:
|
|
94
|
+
f.write(file_content)
|
|
95
|
+
|
|
96
|
+
def _api_request(
|
|
97
|
+
self,
|
|
98
|
+
url: str,
|
|
99
|
+
payload: Optional[dict] = None,
|
|
100
|
+
get: bool = False,
|
|
101
|
+
file: Optional[Any] = None,
|
|
102
|
+
) -> Any:
|
|
103
|
+
"""Send a POST request to the given URL with the given payload
|
|
104
|
+
and return the parsed response. Higher level version of exec_request
|
|
105
|
+
found in RequestUtils.
|
|
106
|
+
|
|
107
|
+
Raises InvalidRequestError on failing requests.
|
|
108
|
+
"""
|
|
109
|
+
return self._request_utils.exec_request(
|
|
110
|
+
self._api_key,
|
|
111
|
+
url,
|
|
112
|
+
payload=payload,
|
|
113
|
+
timeout=self._req_timeout,
|
|
114
|
+
get=get,
|
|
115
|
+
file=file,
|
|
116
|
+
)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dadascribe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python wrapper for the DaDaScribe API
|
|
5
|
+
Project-URL: Homepage, https://api.dadascribe.com/
|
|
6
|
+
Project-URL: Repository, https://github.com/PatzEdi/dadascribe-api-python/
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/PatzEdi/dadascribe-api-python/issues
|
|
8
|
+
Author-email: PatzEdi <patzedigithub@gmail.com>, Fabrizio Ferrari <fabriziovsm@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: diarization,transcriptions,whisper
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.13
|
|
16
|
+
Requires-Dist: requests
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# Python wrapper for the DaDaScribe API
|
|
20
|
+
|
|
21
|
+
The official DaDaScribe API wrapper in Python.
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
## CLI Interface
|
|
26
|
+
The wrapper offers a CLI interface.
|
|
27
|
+
|
|
28
|
+
To start a job, for example:
|
|
29
|
+
```bash
|
|
30
|
+
$ dadascribe --source "link/to/youtube/or/path/to/file"
|
|
31
|
+
{
|
|
32
|
+
"status": "ok",
|
|
33
|
+
"id": "a7wuTaPrebOqheE0",
|
|
34
|
+
"count": 1
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Check a job status:
|
|
39
|
+
```bash
|
|
40
|
+
$ dadascribe --status a7wuTaPrebOqheE0
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Easily download results of a completed job:
|
|
44
|
+
```bash
|
|
45
|
+
$ dadadascribe --download a7wuTaPrebOqheE0
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Python API
|
|
49
|
+
The wrapper offers a Python API.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from dadascribe import ScribeAPIWrapper
|
|
53
|
+
api_key = ... # e.g. from an environment variable, as str
|
|
54
|
+
w = ScribeAPIWrapper(api_key)
|
|
55
|
+
|
|
56
|
+
# Example transcription:
|
|
57
|
+
trsc_result = w.transcribe(
|
|
58
|
+
source="youtube/link/or/path/to/file",
|
|
59
|
+
source_language="en",
|
|
60
|
+
destination_language="es,it",
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
```bash
|
|
67
|
+
pip3 install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Make sure to run the tests as well via the run_tests.sh script:
|
|
71
|
+
```bash
|
|
72
|
+
chmod +x run_tests.sh
|
|
73
|
+
./run_tests.sh
|
|
74
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
dadascribe/__init__.py,sha256=QJsTVPXQJ-3Wqfdl-bkXJd07boanNiJjFHfQlO1mYoc,38
|
|
2
|
+
dadascribe/cli.py,sha256=7Mg7cZbdp5A2_72wU4Fq_a-AtAU6pU1G1o7LS-v8Zyw,4013
|
|
3
|
+
dadascribe/internal_globals.py,sha256=Cs1gUEqdHHNAp9Bwu7ELVcjbTpLvCYlRdvM2GW0HIaM,104
|
|
4
|
+
dadascribe/request_utils.py,sha256=NQEx0VO_2n4uF6ZqYQuFDolFioR4PLflL4aYXp1beg4,4752
|
|
5
|
+
dadascribe/wrapper.py,sha256=_t8vqZH56ZTqL2dctVQCKk-4sYSMvzQ3gEChB131O-g,3674
|
|
6
|
+
dadascribe-0.1.0.dist-info/METADATA,sha256=yE6uyvLRWmpucyjkP_1CwxWlgII2lkVG1Qjejerh95A,1785
|
|
7
|
+
dadascribe-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
8
|
+
dadascribe-0.1.0.dist-info/entry_points.txt,sha256=y7OxHUdmxvz3IWFr6iWycgzJmVrLb4jR3CThx7Ta5wk,51
|
|
9
|
+
dadascribe-0.1.0.dist-info/licenses/LICENSE,sha256=v4gcvKFNHtlz30THehoOU7sWNZrnvnAYMPmUfD3bX9I,1081
|
|
10
|
+
dadascribe-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PatzEdi, Fabrizio Ferrari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|