summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <andreykproductengine@lindenlab.com>2023-02-20 22:06:15 +0200
committerAndrey Kleshchev <andreykproductengine@lindenlab.com>2023-02-20 22:25:04 +0200
commite40f9af831c1257dea37a4565aafd73baa5005b6 (patch)
tree1c479174dd3d2e2e4538e984ccee484e4b4cccd8
parent4f5d7f12563f1829856023d6967507fe783bbe5e (diff)
SL-19108 WIP Managing inventory thumbnail #5
-rw-r--r--indra/newview/llfloaterchangeitemthumbnail.cpp201
-rw-r--r--indra/newview/llpanelprofile.cpp14
-rwxr-xr-xindra/newview/llviewerregion.cpp2
3 files changed, 212 insertions, 5 deletions
diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp
index bd528f43dc..b267c7e0a3 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.cpp
+++ b/indra/newview/llfloaterchangeitemthumbnail.cpp
@@ -38,10 +38,204 @@
#include "lltextbox.h"
#include "lltexturectrl.h"
#include "llthumbnailctrl.h"
+#include "llviewermenufile.h"
#include "llviewerobjectlist.h"
#include "llwindow.h"
+//TODO: this part is likely to be moved into outfit snapshot floater
+// and flaoter is likely to become a thumbnail snapshot floater
+
+#include "llagent.h"
+#include "llnotificationsutil.h"
+#include "llviewertexturelist.h"
+
+static const std::string THUMBNAIL_ITEM_UPLOAD_CAP = "InventoryItemThumbnailUpload";
+static const std::string THUMBNAIL_CATEGORY_UPLOAD_CAP = "InventoryCategoryThumbnailUpload";
+
+void post_thumbnail_image_coro(std::string cap_url, std::string path_to_image, LLSD first_data)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_coro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpHeaders::ptr_t httpHeaders;
+
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+ httpOpts->setFollowRedirects(true);
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (!status)
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
+ return;
+ }
+ if (!result.has("uploader"))
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
+ return;
+ }
+ std::string uploader_cap = result["uploader"].asString();
+ if (uploader_cap.empty())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
+ return;
+ }
+
+ // Upload the image
+
+ LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest);
+ LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders);
+ LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions);
+ S64 length;
+
+ {
+ llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
+ if (!instream.is_open())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
+ return;
+ }
+ length = instream.tellg();
+ }
+
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required!
+ uploaderhttpOpts->setFollowRedirects(true);
+
+ result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
+
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ LL_DEBUGS("Thumbnail") << result << LL_ENDL;
+
+ if (!status)
+ {
+ LL_WARNS("Thumbnail") << "Failed to upload image " << status.toString() << LL_ENDL;
+ return;
+ }
+
+ if (result["state"].asString() != "complete")
+ {
+ if (result.has("message"))
+ {
+ LL_WARNS("Thumbnail") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Thumbnail") << "Failed to upload image " << result << LL_ENDL;
+ }
+ return;
+ }
+
+ // todo: issue an inventory udpate?
+ //return result["new_asset"].asUUID();
+}
+
+class LLThumbnailImagePicker : public LLFilePickerThread
+{
+public:
+ LLThumbnailImagePicker(const LLUUID &item_id, LLHandle<LLFloater> *handle);
+ LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id, LLHandle<LLFloater> *handle);
+ ~LLThumbnailImagePicker();
+ void notify(const std::vector<std::string>& filenames) override;
+
+private:
+ LLHandle<LLFloater> *mHandle;
+ LLUUID mInventoryId;
+ LLUUID mTaskId;
+};
+
+LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, LLHandle<LLFloater> *handle)
+ : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE)
+ , mHandle(handle)
+ , mInventoryId(item_id)
+{
+}
+
+LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id, LLHandle<LLFloater> *handle)
+ : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE)
+ , mHandle(handle)
+ , mInventoryId(item_id)
+ , mTaskId(task_id)
+{
+}
+
+LLThumbnailImagePicker::~LLThumbnailImagePicker()
+{
+ delete mHandle;
+}
+
+void LLThumbnailImagePicker::notify(const std::vector<std::string>& filenames)
+{
+ if (mHandle->isDead())
+ {
+ return;
+ }
+ if (filenames.empty())
+ {
+ return;
+ }
+ std::string file_path = filenames[0];
+ if (file_path.empty())
+ {
+ return;
+ }
+
+ // generate a temp texture file for coroutine
+ std::string temp_file = gDirUtilp->getTempFilename();
+ U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path));
+ const S32 MAX_DIM = 256;
+ if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM))
+ {
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << mInventoryId << " " << mTaskId << ", reason: " << notif_args["REASON"].asString() << LL_ENDL;
+ return;
+ }
+
+ std::string cap_name;
+ LLSD data;
+
+ if (mTaskId.notNull())
+ {
+ cap_name = THUMBNAIL_ITEM_UPLOAD_CAP;
+ data["item_id"] = mInventoryId;
+ data["task_id"] = mTaskId;
+ }
+ else if (gInventory.getCategory(mInventoryId))
+ {
+ cap_name = THUMBNAIL_CATEGORY_UPLOAD_CAP;
+ data["category_id"] = mInventoryId;
+ }
+ else
+ {
+ cap_name = THUMBNAIL_ITEM_UPLOAD_CAP;
+ data["item_id"] = mInventoryId;
+ }
+
+ std::string cap_url = gAgent.getRegionCapability(cap_name);
+ if (cap_url.empty())
+ {
+ LLSD args;
+ args["CAPABILITY"] = cap_url;
+ LLNotificationsUtil::add("RegionCapabilityRequestError", args);
+ LL_WARNS("Thumbnail") << "Failed to upload profile image for item " << mInventoryId << " " << mTaskId << ", no cap found" << LL_ENDL;
+ return;
+ }
+
+ LLCoros::instance().launch("postAgentUserImageCoro",
+ boost::bind(post_thumbnail_image_coro, cap_url, temp_file, data));
+}
+
LLFloaterChangeItemThumbnail::LLFloaterChangeItemThumbnail(const LLSD& key)
: LLFloater(key)
, mObserverInitialized(false)
@@ -236,7 +430,14 @@ void LLFloaterChangeItemThumbnail::refreshFromItem(LLViewerInventoryItem* item)
void LLFloaterChangeItemThumbnail::onUploadLocal(void *userdata)
{
+ LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata;
+ (new LLThumbnailImagePicker(self->mItemId, self->mTaskId, new LLHandle<LLFloater>(self->getHandle())))->getFile();
+ LLFloater* floaterp = self->mPickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
}
void LLFloaterChangeItemThumbnail::onUploadSnapshot(void *userdata)
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index bc3c8ae6b1..078c1370ee 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -366,7 +366,7 @@ LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::stri
httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
- LL_WARNS("AvatarProperties") << result << LL_ENDL;
+ LL_DEBUGS("AvatarProperties") << result << LL_ENDL;
if (!status)
{
@@ -1438,7 +1438,6 @@ void LLPanelProfileSecondLife::setLoaded()
}
-
class LLProfileImagePicker : public LLFilePickerThread
{
public:
@@ -1485,15 +1484,20 @@ void LLProfileImagePicker::notify(const std::vector<std::string>& filenames)
const S32 MAX_DIM = 256;
if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM))
{
- //todo: image not supported notification
- LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", failed to open image" << LL_ENDL;
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", " << notif_args["REASON"].asString() << LL_ENDL;
return;
}
std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
if (cap_url.empty())
{
- LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", no cap found" << LL_ENDL;
+ LLSD args;
+ args["CAPABILITY"] = PROFILE_IMAGE_UPLOAD_CAP;
+ LLNotificationsUtil::add("RegionCapabilityRequestError", args);
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", no cap found" << LL_ENDL;
return;
}
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index ad7321ca4b..e5d703a6ee 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3024,6 +3024,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("InterestList");
+ capabilityNames.append("InventoryCategoryThumbnailUpload");
+ capabilityNames.append("InventoryItemThumbnailUpload");
capabilityNames.append("GetDisplayNames");
capabilityNames.append("GetExperiences");
capabilityNames.append("AgentExperiences");