Blossom
low level
CLI
— ndk blossom upload|download|delete|list|mirror|check|servers
Example
final downloadResult = await ndk.blossom.getBlob(
sha256:
"b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553",
serverUrls: ["https://cdn.hzrd149.com", "https://nostr.download"],
);
print(
"file of type: ${downloadResult.mimeType}, size: ${downloadResult.data.length}",
);
When to use
For a simpler, more generic API, check out
If no servers are specified the default user server list (kind 10063) is used for upload and delete.
The auth events get automatically signed and are valid for:
const Duration BLOSSOM_AUTH_EXPIRATION = Duration(minutes: 5);
methods - Blossom
uploadBlob
upload a blob, if serverMediaOptimisation is set to true the /media endpoint is used.
/// Gets the signer to use for blossom operations
/// Priority: customSigner > logged in account signer > temporary signer
EventSigner _getSigner(EventSigner? customSigner) {
if (customSigner != null) return customSigner;
if (_accounts.canSign) {
return _accounts.getLoggedAccount()!.signer;
}
// Create a temporary signer if no account is logged in
final keyPair = Bip340.generatePrivateKey();
return _eventSignerFactory.create(
getBlob
Download the blob and use fallback if the blob is not found or the server is offline.
["x", dataSha256],
["expiration", "${now + BLOSSOM_AUTH_EXPIRATION.inMilliseconds}"],
],
);
final signedAuthorization = await signer.sign(myAuthorization);
serverUrls ??= await _userServerList.getUserServerList(
pubkeys: [signer.getPublicKey()],
checkBlob
if you have a video player that uses a url you can use check to get a valid url first. Example can be found in NDK demo app
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final signer = _getSigner(customSigner);
String? fileHash = precomputedSha256;
if (fileHash == null) {
// Compute file hash without loading entire file into memory
await for (final hashProgress in _blossomImpl.computeFileHash(filePath)) {
yield BlobUploadProgress(
currentServer: '',
sentBytes: hashProgress.processedBytes,
getBlobStream
Similar to getBlob, it streams the data, which is helpful for video files.
serverUrls: serverUrls,
authorization: signedAuthorization,
contentType: contentType,
strategy: strategy,
mediaOptimisation: serverMediaOptimisation,
);
}
/// Mirror a blob from a blossom URL to specified servers using the blossom /mirror endpoint
///
listBlobs
targetServerUrls.map(
(serverUrl) => _blossomImpl.mirrorToServer(
fileUrl: blossomUrl.toString(),
serverUrl: serverUrl,
sha256: sha256,
authorization: signedAuthorization,
),
),
);
return results;
deleteBlob
throw Exception(
"pubkeyToFetchUserServerList is null and serverUrls is null",
);
}
serverUrls ??= await _userServerList.getUserServerList(
pubkeys: [pubkeyToFetchUserServerList],
);
directDownload
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
myAuthorization = Nip01Utils.createEventCalculateId(
content: "get",
report
tags: [
["t", "get"],
["x", sha256],
["expiration", "${now + BLOSSOM_AUTH_EXPIRATION.inMilliseconds}"],
],
);
signedAuthorization = await signer.sign(myAuthorization);
}
if (serverUrls == null) {
if (pubkeyToFetchUserServerList == null) {
throw Exception(
"pubkeyToFetchUserServerList is null and serverUrls is null",
);
methods - BlossomUserServerList
To get and set the user server list e.g. on settings page, you can use BlossomUserServerList
getUserServerList
/// Get user server list \
/// returns list of server urls \
/// returns null if the user has no server list
Future<List<String>?> getUserServerList({
required List<String> pubkeys,
}) async {
publishUserServerList
required List<String> serverUrlsOrdered,
}) async {
if (serverUrlsOrdered.isEmpty) {
throw Exception("serverUrlsOrdered is empty");
}