REFACTORING DIRECTORIES
This commit is contained in:
1 parent
c645019380
commit
c8e1d5046c
54 files changed
+6193
-747
No files matched your search
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from tonsdk.boc import Cell\n",
|
||||
"from base64 import b64decode"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s = Cell.one_from_boc(b64decode(\n",
|
||||
" \"te6cckECCwEAAWcAAotUkdCMcWNw723qPXgUdCcgt8COtmF/zIqTQpNUO7Hx3cFkE6SAHuuAXC2ImbR2RbW6eaLmYTaiOFzd06Q3v6GEw1xpHOswAQUCAaACAwALAECPDRgIAQHRBABHgB7rgFwtiJm0dkW1unmi5mE2ojhc3dOkN7+hhMNcaRzrJOIQAgAGBwD6aHR0cHM6Ly9teS1wdWJsaWMtbm9kZS0xLnByb2pzY2FsZS5kZXYvYXBpL3YxLjUvc3RvcmFnZS85S0h5RHAyYUppRTRuaG1uOExxazd4dmZxeWpwQXdXR1dYU2ozMkNVQW04V3NLdVFLSkJlTktIV0prZzhyZFloTm82MzcDAAgJCgBYajZGa3ZtREN1RUJhOHhrTDlpV1NSem1peXAxSGhYMlJkUFNFWWZpb1JRMWgAAABYblJUVW96dGJBcXF0Z25EWEN1cFU4UXRZWmFtY1dxdjlSTDQ0UURYbVpvQVbtCcTY\"\n",
|
||||
"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s = s.begin_parse()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'AddressEQD3XALhbETNo7ItrdPNFzMJtRHC5u6dIb39DCYa40jnWcql'"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"s.read_uint(32)\n",
|
||||
"s.read_uint(256)\n",
|
||||
"'Address' + s.read_msg_addr().to_string(1, 1, 1)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ValueError",
|
||||
"evalue": "invalid literal for int() with base 10: 'b'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAddress\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m+\u001b[39m s\u001b[38;5;241m.\u001b[39mread_msg_addr()\u001b[38;5;241m.\u001b[39mto_string(\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n",
|
||||
"File \u001b[0;32m/opt/anaconda3/lib/python3.12/site-packages/tonsdk/boc/_slice.py:93\u001b[0m, in \u001b[0;36mSlice.read_msg_addr\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 91\u001b[0m workchain_id \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mhex\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mread_int(\u001b[38;5;241m8\u001b[39m))\u001b[38;5;241m.\u001b[39mreplace(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m0x\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 92\u001b[0m hashpart \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mread_bytes(\u001b[38;5;241m32\u001b[39m)\u001b[38;5;241m.\u001b[39mhex()\n\u001b[0;32m---> 93\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Address(workchain_id \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m:\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m hashpart)\n",
|
||||
"File \u001b[0;32m/opt/anaconda3/lib/python3.12/site-packages/tonsdk/utils/_address.py:87\u001b[0m, in \u001b[0;36mAddress.__init__\u001b[0;34m(self, any_form)\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(arr) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[1;32m 85\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m InvalidAddressError(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInvalid address \u001b[39m\u001b[38;5;132;01m{\u001b[39;00many_form\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 87\u001b[0m wc \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(arr[\u001b[38;5;241m0\u001b[39m])\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m wc \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m wc \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 89\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m InvalidAddressError(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInvalid address wc \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mwc\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
|
||||
"\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'b'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"'Address' + s.read_msg_addr().to_string(1, 1, 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s = Cell.one_from_boc('b5ee9c7201020b0100016700028b5491d08c716370ef6dea3d7814742720b7c08eb6617fcc8a934293543bb1f1ddc16413a4801eeb805c2d8899b47645b5ba79a2e66136a2385cddd3a437bfa184c35c691ceb3001020201a0030402000607000b00408f0d18080101d1050047801eeb805c2d8899b47645b5ba79a2e66136a2385cddd3a437bfa184c35c691ceb24e21000fa68747470733a2f2f6d792d7075626c69632d6e6f64652d312e70726f6a7363616c652e6465762f6170692f76312e352f73746f726167652f394b4879447032614a6945346e686d6e384c716b3778766671796a70417757475758536a33324355416d3857734b75514b4a42654e4b48574a6b6738726459684e6f363337030008090a00586a36466b766d44437545426138786b4c39695753527a6d697970314868583252645053455966696f52513168000000586e5254556f7a746241717174676e445843757055385174595a616d6357717639524c34345144586d5a6f4156')\n",
|
||||
"s = s.begin_parse()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"3"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\n",
|
||||
"s.read_uint(256)\n",
|
||||
"s.read_uint(2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'0b11'"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"bin(3)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "base",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Загрузка и стриминг файла</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
section { margin-bottom: 40px; }
|
||||
label { display: block; margin-bottom: 5px; }
|
||||
input, button { margin-bottom: 10px; }
|
||||
#log { border: 1px solid #ccc; padding: 10px; max-height: 200px; overflow-y: auto; background: #f9f9f9; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Загрузка и стриминг файла</h1>
|
||||
|
||||
<section id="uploadSection">
|
||||
<h2>Загрузка файла</h2>
|
||||
<label for="uploadFile">Выберите файл для загрузки:</label>
|
||||
<input type="file" id="uploadFile">
|
||||
<br>
|
||||
<button id="uploadBtn">Загрузить файл</button>
|
||||
<div id="uploadResult"></div>
|
||||
</section>
|
||||
|
||||
<section id="streamSection">
|
||||
<h2>Стриминг файла</h2>
|
||||
<label for="fileHashInput">Введите file_hash:</label>
|
||||
<input type="text" id="fileHashInput" placeholder="Введите hash">
|
||||
<br>
|
||||
<button id="loadFileBtn">Загрузить файл для стриминга</button>
|
||||
<div id="mediaContainer" style="margin-top:20px;"></div>
|
||||
</section>
|
||||
|
||||
<section id="logSection">
|
||||
<h2>Лог</h2>
|
||||
<div id="log"></div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
// Base URL for endpoints
|
||||
const BASE_URL = "https://my-public-node-1.projscale.dev/api/v1.5/storage";
|
||||
|
||||
// Append log message to log div
|
||||
function appendLog(message) {
|
||||
const logDiv = document.getElementById('log');
|
||||
const p = document.createElement('p');
|
||||
p.textContent = message;
|
||||
logDiv.appendChild(p);
|
||||
}
|
||||
|
||||
// Upload file in chunks (max 80 MB per chunk) without computing file hash
|
||||
async function uploadFileInChunks(file) {
|
||||
const maxChunkSize = 80 * 1024 * 1024; // 80 MB
|
||||
let offset = 0;
|
||||
let uploadId = null;
|
||||
appendLog("Starting file upload...");
|
||||
|
||||
while (offset < file.size) {
|
||||
const chunk = file.slice(offset, Math.min(offset + maxChunkSize, file.size));
|
||||
appendLog(`Uploading chunk starting at byte ${offset}`);
|
||||
|
||||
// Prepare headers for the chunk upload
|
||||
const headers = {
|
||||
"X-File-Name": btoa(unescape(encodeURIComponent(file.name))), // File name in base64
|
||||
"X-Chunk-Start": offset.toString(),
|
||||
"Content-Type": file.type || "application/octet-stream"
|
||||
};
|
||||
if (uploadId) {
|
||||
headers["X-Upload-ID"] = uploadId;
|
||||
}
|
||||
// Set header to indicate the last chunk if this is the final part of the file
|
||||
if (offset + chunk.size >= file.size) {
|
||||
headers["X-Last-Chunk"] = "1";
|
||||
}
|
||||
|
||||
const response = await fetch(BASE_URL, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: chunk
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
appendLog(`Chunk upload failed: ${errorData.error}`);
|
||||
throw new Error(`Upload failed at offset ${offset}: ${errorData.error}`);
|
||||
}
|
||||
|
||||
const resultData = await response.json();
|
||||
// Save uploadId from first response if not set
|
||||
if (!uploadId && resultData.upload_id) {
|
||||
uploadId = resultData.upload_id;
|
||||
}
|
||||
|
||||
// If final response contains content_id, upload is complete
|
||||
if (resultData.content_id) {
|
||||
appendLog(`Upload complete. File ID: ${resultData.content_id}`);
|
||||
return resultData;
|
||||
}
|
||||
|
||||
// Update offset based on server-reported current size
|
||||
if (resultData.current_size !== undefined) {
|
||||
offset = resultData.current_size;
|
||||
appendLog(`Server reports current_size: ${offset}`);
|
||||
} else {
|
||||
appendLog("Unexpected response from server, missing current_size.");
|
||||
throw new Error("Missing current_size in response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upload button event listener
|
||||
document.getElementById('uploadBtn').addEventListener('click', async () => {
|
||||
const fileInput = document.getElementById('uploadFile');
|
||||
const uploadResult = document.getElementById('uploadResult');
|
||||
uploadResult.textContent = "";
|
||||
|
||||
if (!fileInput.files || fileInput.files.length === 0) {
|
||||
uploadResult.textContent = "Пожалуйста, выберите файл.";
|
||||
return;
|
||||
}
|
||||
|
||||
const file = fileInput.files[0];
|
||||
try {
|
||||
const resultData = await uploadFileInChunks(file);
|
||||
uploadResult.textContent = `Файл загружен успешно. content_sha256: ${resultData.content_sha256}`;
|
||||
} catch (err) {
|
||||
uploadResult.textContent = "Ошибка при загрузке файла.";
|
||||
}
|
||||
});
|
||||
|
||||
// Load file for streaming (remains unchanged)
|
||||
document.getElementById('loadFileBtn').addEventListener('click', async () => {
|
||||
const fileHash = document.getElementById('fileHashInput').value.trim();
|
||||
const mediaContainer = document.getElementById('mediaContainer');
|
||||
mediaContainer.innerHTML = "";
|
||||
|
||||
if (!fileHash) {
|
||||
mediaContainer.textContent = "Пожалуйста, введите file_hash.";
|
||||
return;
|
||||
}
|
||||
|
||||
const fileUrl = `${BASE_URL}/${fileHash}`;
|
||||
appendLog(`Fetching file info for hash: ${fileHash}`);
|
||||
|
||||
try {
|
||||
const headResponse = await fetch(fileUrl, { method: "HEAD" });
|
||||
if (!headResponse.ok) {
|
||||
mediaContainer.textContent = "Файл не найден.";
|
||||
appendLog("File not found during HEAD request.");
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = headResponse.headers.get("Content-Type") || "";
|
||||
appendLog(`Content-Type: ${contentType}`);
|
||||
|
||||
let mediaElement;
|
||||
if (contentType.startsWith("image/")) {
|
||||
mediaElement = document.createElement("img");
|
||||
mediaElement.style.maxWidth = "100%";
|
||||
} else if (contentType.startsWith("video/")) {
|
||||
mediaElement = document.createElement("video");
|
||||
mediaElement.controls = true;
|
||||
mediaElement.style.maxWidth = "100%";
|
||||
} else if (contentType.startsWith("audio/")) {
|
||||
mediaElement = document.createElement("audio");
|
||||
mediaElement.controls = true;
|
||||
} else {
|
||||
mediaElement = document.createElement("a");
|
||||
mediaElement.textContent = "Скачать файл";
|
||||
}
|
||||
|
||||
if (mediaElement.tagName === "A") {
|
||||
mediaElement.href = fileUrl;
|
||||
mediaElement.download = "";
|
||||
} else {
|
||||
mediaElement.src = fileUrl;
|
||||
}
|
||||
|
||||
mediaContainer.appendChild(mediaElement);
|
||||
appendLog("Media element created and added to the page.");
|
||||
} catch (err) {
|
||||
mediaContainer.textContent = "Ошибка при загрузке файла.";
|
||||
appendLog(`Error during file streaming: ${err}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user