HaikuDepot: Populate package icons from web app.

* Transformed ConnectionTest into WebAppInterface
 * Added methods to Model to trigger the population of all packages
   with additional information that is needed in the list view.
 * This launches a thread which tries to retrieve icons for all known
   packages from depot.haiku-os.org. This is uncached and very slow. I
   guess it could even be fast enough with a change to the protocol where
   icons are not retrieved one at a time.
This commit is contained in:
Stephan Aßmus
2014-03-28 23:49:29 +01:00
parent b34f1a8b11
commit 5bd5f161e8
8 changed files with 351 additions and 254 deletions
-214
View File
@@ -1,214 +0,0 @@
/*
* Copyright 2014, Stephan Aßmus <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ConnectionTest.h"
#include <stdio.h>
#include <mail_encoding.h>
#include <File.h>
#include <HttpHeaders.h>
#include <HttpRequest.h>
#include <Url.h>
#include <UrlContext.h>
#include <UrlProtocolListener.h>
#include <UrlProtocolRoster.h>
class ProtocolListener : public BUrlProtocolListener {
public:
ProtocolListener::ProtocolListener()
:
fDownloadIO(NULL)
{
}
virtual void ConnectionOpened(BUrlRequest* caller)
{
printf("ConnectionOpened(%p)\n", caller);
}
virtual void HostnameResolved(BUrlRequest* caller, const char* ip)
{
printf("HostnameResolved(%p): %s\n", caller, ip);
}
virtual void ResponseStarted(BUrlRequest* caller)
{
printf("ResponseStarted(%p)\n", caller);
}
virtual void HeadersReceived(BUrlRequest* caller)
{
printf("HeadersReceived(%p)\n", caller);
}
virtual void DataReceived(BUrlRequest* caller, const char* data,
ssize_t size)
{
printf("DataReceived(%p): %ld bytes\n", caller, size);
if (fDownloadIO != NULL)
fDownloadIO->Write(data, size);
}
virtual void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived,
ssize_t bytesTotal)
{
printf("DownloadProgress(%p): %ld/%ld\n", caller, bytesReceived,
bytesTotal);
}
virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent,
ssize_t bytesTotal)
{
printf("UploadProgress(%p): %ld/%ld\n", caller, bytesSent, bytesTotal);
}
virtual void RequestCompleted(BUrlRequest* caller, bool success)
{
printf("RequestCompleted(%p): %d\n", caller, success);
}
virtual void DebugMessage(BUrlRequest* caller,
BUrlProtocolDebugMessage type, const char* text)
{
printf("DebugMessage(%p): %s\n", caller, text);
}
void SetDownloadIO(BDataIO* downloadIO)
{
fDownloadIO = downloadIO;
}
private:
BDataIO* fDownloadIO;
};
ConnectionTest::ConnectionTest(const char* username, const char* password)
:
BApplication("application/x-vnd.Haiku-HaikuDepot-ConnectionTest"),
fUsername(username),
fPassword(password)
{
}
ConnectionTest::~ConnectionTest()
{
}
void
ConnectionTest::ReadyToRun()
{
printf("Connecting...\n");
_TestDownloadIcon();
PostMessage(B_QUIT_REQUESTED, this);
}
void
ConnectionTest::_TestGetPackage()
{
BUrl url("https://depot.haiku-os.org/api/v1/pkg");
ProtocolListener listener;
BUrlContext context;
BHttpHeaders headers;
// Authentication
if (!fUsername.IsEmpty() && !fPassword.IsEmpty()) {
BString plain;
plain << fUsername << ':' << fPassword;
char base64[1024];
ssize_t base64Size = encode_base64(base64, plain, plain.Length(),
false);
if (base64Size > 0 && base64Size <= (ssize_t)sizeof(base64)) {
base64[base64Size] = '\0';
BString authorization("Basic ");
authorization << base64;
headers.AddHeader("Authorization", authorization);
}
}
// Content-Type
headers.AddHeader("Content-Type", "application/json");
BHttpRequest request(url, true, "HTTP", &listener, &context);
request.SetMethod(B_HTTP_POST);
request.SetHeaders(headers);
BMallocIO* data = new BMallocIO();
BString jsonString
= "{"
"\"jsonrpc\":\"2.0\","
"\"id\":4143431,"
"\"method\":\"getPkg\","
"\"params\":[{"
"\"name\":\"apr\","
"\"architectureCode\":\"x86\","
"\"versionType\":\"NONE\""
"}]"
"}"
;
data->WriteAt(0, jsonString.String(), jsonString.Length());
data->Seek(0, SEEK_SET);
request.AdoptInputData(data);
thread_id thread = request.Run();
wait_for_thread(thread, NULL);
}
void
ConnectionTest::_TestDownloadIcon()
{
BUrl url("https://depot.haiku-os.org/pkgicon/wonderbrush_x86_gcc2.hvif");
BFile file("/boot/home/Desktop/wonderbrush.hvif",
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
ProtocolListener listener;
listener.SetDownloadIO(&file);
// Content-Type
BHttpRequest request(url, true, "HTTP", &listener);
request.SetMethod(B_HTTP_GET);
thread_id thread = request.Run();
wait_for_thread(thread, NULL);
}
int
main(int argc, char* argv[])
{
const char* username = "";
const char* password = "";
for (int i = 1; i < argc; i++) {
if (strcmp("-u", argv[i]) == 0 && i < argc - 1)
username = argv[++i];
else if (strcmp("-p", argv[i]) == 0 && i < argc - 1)
password = argv[++i];
}
printf("Using username: %s, password: %s\n", username, password);
ConnectionTest(username, password).Run();
return 0;
}
-30
View File
@@ -1,30 +0,0 @@
/*
* Copyright 2014, Stephan Aßmus <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef CONNECTION_TEST_H
#define CONNECTION_TEST_H
#include <Application.h>
#include <String.h>
class ConnectionTest : public BApplication {
public:
ConnectionTest(const char* username,
const char* password);
virtual ~ConnectionTest();
virtual void ReadyToRun();
private:
void _TestGetPackage();
void _TestDownloadIcon();
private:
BString fUsername;
BString fPassword;
};
#endif // CONNECTION_TEST_H
+2 -7
View File
@@ -53,6 +53,7 @@ Application HaikuDepot :
PackageManager.cpp
RatePackageWindow.cpp
support.cpp
WebAppInterface.cpp
# package_daemon
ProblemWindow.cpp
@@ -61,7 +62,7 @@ Application HaikuDepot :
# text view stuff
$(textDocumentSources)
: be package translation libcolumnlistview.a libshared.a
: be package netapi translation libcolumnlistview.a libshared.a
$(TARGET_LIBSTDC++) $(TARGET_LIBSUPC++) localestub
: HaikuDepot.rdef
;
@@ -85,9 +86,3 @@ Application TextDocumentTest :
: be translation libshared.a $(TARGET_LIBSUPC++)
;
Application ConnectionTest :
ConnectionTest.cpp
: be netapi translation libshared.a $(TARGET_LIBSUPC++)
;
+3
View File
@@ -580,6 +580,9 @@ MainWindow::_RefreshPackageList()
fModel.AddDepot(it->second);
}
// start retrieving package icons and average ratings
fModel.PopulateAllPackages();
// compute the OS package dependencies
try {
// create the solver
+102 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2014, Axel Dörfler <axeld@pinc-software.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -12,6 +12,8 @@
#include <Autolock.h>
#include <Catalog.h>
#include "WebAppInterface.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Model"
@@ -281,7 +283,10 @@ Model::Model()
fSearchTermsFilter(PackageFilterRef(new AnyFilter(), true)),
fShowSourcePackages(false),
fShowDevelopPackages(false)
fShowDevelopPackages(false),
fPopulateAllPackagesThread(-1),
fStopPopulatingAllPackages(false)
{
// Don't forget to add new categories to this list:
fCategories.Add(fCategoryAudio);
@@ -327,6 +332,12 @@ Model::Model()
}
Model::~Model()
{
StopPopulatingAllPackages();
}
PackageList
Model::CreatePackageList() const
{
@@ -368,6 +379,7 @@ Model::AddDepot(const DepotInfo& depot)
void
Model::Clear()
{
StopPopulatingAllPackages();
fDepots.Clear();
}
@@ -520,3 +532,91 @@ Model::PopulatePackage(const PackageInfoRef& package, uint32 flags)
}
}
void
Model::PopulateAllPackages()
{
StopPopulatingAllPackages();
fStopPopulatingAllPackages = false;
fPopulateAllPackagesThread = spawn_thread(&_PopulateAllPackagesEntry,
"Package populator", B_NORMAL_PRIORITY, this);
if (fPopulateAllPackagesThread >= 0)
resume_thread(fPopulateAllPackagesThread);
}
void
Model::StopPopulatingAllPackages()
{
if (fPopulateAllPackagesThread < 0)
return;
fStopPopulatingAllPackages = true;
wait_for_thread(fPopulateAllPackagesThread, NULL);
fPopulateAllPackagesThread = -1;
}
int32
Model::_PopulateAllPackagesEntry(void* cookie)
{
Model* model = static_cast<Model*>(cookie);
model->_PopulateAllPackagesThread();
return 0;
}
void
Model::_PopulateAllPackagesThread()
{
int32 depotIndex = 0;
int32 packageIndex = 0;
while (!fStopPopulatingAllPackages) {
// Obtain PackageInfoRef while keeping the depot and package lists
// locked.
PackageInfoRef package;
{
BAutolock locker(&fLock);
if (depotIndex >= fDepots.CountItems())
break;
const DepotInfo& depot = fDepots.ItemAt(depotIndex);
const PackageList& packages = depot.Packages();
if (packageIndex >= packages.CountItems()) {
// Need the next depot
packageIndex = 0;
depotIndex++;
continue;
}
package = packages.ItemAt(packageIndex);
packageIndex++;
}
if (package.Get() == NULL)
continue;
_PopulatePackageIcon(package);
// TODO: Average user rating. It needs to be shown in the
// list view, so without the user clicking the package.
}
}
void
Model::_PopulatePackageIcon(const PackageInfoRef& package)
{
WebAppInterface interface;
BMallocIO buffer;
status_t status = interface.RetrievePackageIcon(package->Title(), &buffer);
if (status == B_OK) {
BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
package->SetIcon(bitmapRef);
}
}
+14 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MODEL_H
@@ -24,6 +24,7 @@ typedef BReference<PackageFilter> PackageFilterRef;
class Model {
public:
Model();
virtual ~Model();
BLocker* Lock()
{ return &fLock; }
@@ -85,6 +86,15 @@ public:
void PopulatePackage(const PackageInfoRef& package,
uint32 flags);
void PopulateAllPackages();
void StopPopulatingAllPackages();
private:
static int32 _PopulateAllPackagesEntry(void* cookie);
void _PopulateAllPackagesThread();
void _PopulatePackageIcon(
const PackageInfoRef& package);
private:
BLocker fLock;
@@ -117,6 +127,9 @@ private:
bool fShowSourcePackages;
bool fShowDevelopPackages;
thread_id fPopulateAllPackagesThread;
volatile bool fStopPopulatingAllPackages;
};
+196
View File
@@ -0,0 +1,196 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "WebAppInterface.h"
#include <stdio.h>
#include <File.h>
#include <HttpHeaders.h>
#include <HttpRequest.h>
#include <Url.h>
#include <UrlContext.h>
#include <UrlProtocolListener.h>
#include <UrlProtocolRoster.h>
#include "PackageInfo.h"
class ProtocolListener : public BUrlProtocolListener {
public:
ProtocolListener::ProtocolListener()
:
fDownloadIO(NULL)
{
}
virtual void ConnectionOpened(BUrlRequest* caller)
{
// printf("ConnectionOpened(%p)\n", caller);
}
virtual void HostnameResolved(BUrlRequest* caller, const char* ip)
{
// printf("HostnameResolved(%p): %s\n", caller, ip);
}
virtual void ResponseStarted(BUrlRequest* caller)
{
// printf("ResponseStarted(%p)\n", caller);
}
virtual void HeadersReceived(BUrlRequest* caller)
{
// printf("HeadersReceived(%p)\n", caller);
}
virtual void DataReceived(BUrlRequest* caller, const char* data,
ssize_t size)
{
// printf("DataReceived(%p): %ld bytes\n", caller, size);
if (fDownloadIO != NULL)
fDownloadIO->Write(data, size);
}
virtual void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived,
ssize_t bytesTotal)
{
// printf("DownloadProgress(%p): %ld/%ld\n", caller, bytesReceived,
// bytesTotal);
}
virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent,
ssize_t bytesTotal)
{
// printf("UploadProgress(%p): %ld/%ld\n", caller, bytesSent, bytesTotal);
}
virtual void RequestCompleted(BUrlRequest* caller, bool success)
{
// printf("RequestCompleted(%p): %d\n", caller, success);
}
virtual void DebugMessage(BUrlRequest* caller,
BUrlProtocolDebugMessage type, const char* text)
{
// printf("DebugMessage(%p): %s\n", caller, text);
}
void SetDownloadIO(BDataIO* downloadIO)
{
fDownloadIO = downloadIO;
}
private:
BDataIO* fDownloadIO;
};
WebAppInterface::WebAppInterface()
{
}
WebAppInterface::~WebAppInterface()
{
}
void
WebAppInterface::SetAuthorization(const BString& username,
const BString& password)
{
fUsername = username;
fPassword = password;
}
//void
//WebAppInterface::_TestGetPackage()
//{
// BUrl url("https://depot.haiku-os.org/api/v1/pkg");
//
// ProtocolListener listener;
// BUrlContext context;
// BHttpHeaders headers;
//
// // Authentication
// if (!fUsername.IsEmpty() && !fPassword.IsEmpty()) {
// BString plain;
// plain << fUsername << ':' << fPassword;
//
// char base64[1024];
// ssize_t base64Size = encode_base64(base64, plain, plain.Length(),
// false);
//
// if (base64Size > 0 && base64Size <= (ssize_t)sizeof(base64)) {
// base64[base64Size] = '\0';
//
// BString authorization("Basic ");
// authorization << base64;
//
// headers.AddHeader("Authorization", authorization);
// }
// }
// // Content-Type
// headers.AddHeader("Content-Type", "application/json");
//
// BHttpRequest request(url, true, "HTTP", &listener, &context);
// request.SetMethod(B_HTTP_POST);
// request.SetHeaders(headers);
//
// BMallocIO* data = new BMallocIO();
//
// BString jsonString
// = "{"
// "\"jsonrpc\":\"2.0\","
// "\"id\":4143431,"
// "\"method\":\"getPkg\","
// "\"params\":[{"
// "\"name\":\"apr\","
// "\"architectureCode\":\"x86\","
// "\"versionType\":\"NONE\""
// "}]"
// "}"
// ;
//
// data->WriteAt(0, jsonString.String(), jsonString.Length());
// data->Seek(0, SEEK_SET);
// request.AdoptInputData(data);
//
// thread_id thread = request.Run();
// wait_for_thread(thread, NULL);
//}
status_t
WebAppInterface::RetrievePackageIcon(const BString& packageName,
BDataIO* stream)
{
BString urlString = "https://depot.haiku-os.org/pkgicon/";
urlString << packageName << ".hvif";
BUrl url(urlString);
ProtocolListener listener;
listener.SetDownloadIO(stream);
BHttpRequest request(url, true, "HTTP", &listener);
request.SetMethod(B_HTTP_GET);
thread_id thread = request.Run();
wait_for_thread(thread, NULL);
const BHttpResult& result = dynamic_cast<const BHttpResult&>(
request.Result());
int32 statusCode = result.StatusCode();
if (statusCode == 200)
return B_OK;
return B_ERROR;
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef WEB_APP_INTERFACE_H
#define WEB_APP_INTERFACE_H
#include <Application.h>
#include <String.h>
class BDataIO;
class WebAppInterface {
public:
WebAppInterface();
virtual ~WebAppInterface();
void SetAuthorization(const BString& username,
const BString& password);
status_t RetrievePackageIcon(
const BString& packageName,
BDataIO* stream);
private:
BString fUsername;
BString fPassword;
};
#endif // WEB_APP_INTERFACE_H