Did async download of images

This commit is contained in:
Paul 2018-02-28 17:13:29 +01:00
parent 8657d27949
commit bc13750209
12 changed files with 212 additions and 67 deletions

View File

@ -28,14 +28,6 @@ m_builder(nullptr)
// See https://stackoverflow.com/questions/9192223/remove-gtk-container-children-repopulate-it-then-refresh
void MainPickerWindow::reset_game_list() {
/*GList *children, *iter;
children = gtk_container_get_children(GTK_CONTAINER(m_game_list));
for(iter = children; iter != NULL; iter = g_list_next(iter))
gtk_widget_destroy(GTK_WIDGET(iter->data));
g_list_free(children);*/
gtk_container_foreach(GTK_CONTAINER(m_game_list), (GtkCallback)gtk_widget_destroy, NULL);
//TODO refresh the view but I dont know how to
@ -60,6 +52,7 @@ void MainPickerWindow::add_to_game_list(const Game_t& app) {
#pragma GCC diagnostic pop
gtk_widget_set_size_request(wrapper, -1, 80);
gtk_buildable_set_name(GTK_BUILDABLE(game_logo), std::to_string(app.app_id).c_str());
gtk_box_pack_start(GTK_BOX(layout), GTK_WIDGET(game_logo), FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(layout), GTK_WIDGET(label), TRUE, TRUE, 0);
@ -74,4 +67,29 @@ void MainPickerWindow::add_to_game_list(const Game_t& app) {
void MainPickerWindow::confirm_game_list() {
gtk_widget_show_all(GTK_WIDGET(m_game_list));
// Show all widgetss
}
}
void
MainPickerWindow::refresh_app_icon(const unsigned long app_id) {
std::cerr << "Gotta renew " << app_id << "'s icon" << std::endl;
GList *children, *iter;
char id[256];
char req_id[256];
children = gtk_container_get_children(GTK_CONTAINER(m_game_list));
strncpy(req_id, std::to_string(app_id).c_str(), 256);
std::cerr << "length: " << g_list_length(children); //0 TO DEBUG
for(iter = children; iter != NULL; iter = g_list_next(iter)) {
strncpy(id, gtk_buildable_get_name(GTK_BUILDABLE(iter->data)), 256);
if(strcmp(id, req_id) == 0) {
//TODO
}
}
g_list_free(children);
}

View File

@ -6,11 +6,12 @@
class MainPickerWindow {
public:
//Todo: destructor
//Todo: destructor and tidy
MainPickerWindow();
void reset_game_list();
void add_to_game_list(const Game_t& app);
void confirm_game_list();
void refresh_app_icon(const unsigned long app_id);
GtkWidget* get_main_window() { return m_main_window; };
private:

View File

@ -82,9 +82,10 @@ void MySteam::refresh_owned_apps() {
const std::string input_scheme_c(prefix + "%lu.bin");
Game_t game;
unsigned long app_id;
SteamAppDAO* appDAO = SteamAppDAO::get_instance();
// The whole update will really occur only once in a while, no worries
SteamAppDAO::update_name_database();
appDAO->update_name_database();
m_all_subscribed_apps.clear();
while ((dp = readdir(dirp)) != NULL) {
@ -92,8 +93,8 @@ void MySteam::refresh_owned_apps() {
if(filename.rfind(prefix, 0) == 0) {
if(sscanf(dp->d_name, input_scheme_c.c_str(), &app_id) == 1) {
game.app_id = app_id;
game.app_name = SteamAppDAO::get_app_name(app_id);
SteamAppDAO::download_app_icon(app_id);
game.app_name = appDAO->get_app_name(app_id);
appDAO->download_app_icon(app_id);
m_all_subscribed_apps.push_back(game);
}

View File

@ -49,7 +49,7 @@ SteamAppDAO::update_name_database() {
}
if(need_to_redownload) {
SteamAppDAO::download_file("http://api.steampowered.com/ISteamApps/GetAppList/v0002/", local_file_name);
Downloader::get_instance()->download_file("http://api.steampowered.com/ISteamApps/GetAppList/v0002/", local_file_name, 0);
SteamAppDAO::parse_app_names();
}
}
@ -72,43 +72,7 @@ SteamAppDAO::download_app_icon(const unsigned long& app_id) {
exit(EXIT_FAILURE);
}
if(!file_exists(local_path)){
SteamAppDAO::download_file(url, local_path);
}
}
void
SteamAppDAO::download_file(const std::string& file_url, const std::string& local_path) {
std::cerr << "Downloading " << file_url << std::endl;
CURL *curl;
FILE *fp;
CURLcode res;
curl = curl_easy_init();
if (curl) {
fp = fopen(local_path.c_str(),"wb");
curl_easy_setopt(curl, CURLOPT_URL, file_url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
else {
std::cerr << "An error occurred creating curl. Please report to the developers!" << std::endl;
exit(EXIT_FAILURE);
}
if(res != 0) {
std::cerr << "Curl returned with status " << res << ", which is unexpected. Make sure you are connected to the internet, and you have access";
std::cerr << " to Steam, and try again." << std::endl;
std::cerr << "Unable to fetch file " << file_url << std::endl;
//TODO make a gui for this
exit(EXIT_FAILURE);
}
Downloader::get_instance()->download_file_async(url, local_path, app_id);
}
void
@ -162,4 +126,9 @@ SteamAppDAO::parse_app_names() {
std::cerr << "Could not retrieve app names. Unable to open " << file_path << std::endl;
exit(EXIT_FAILURE);
}
}
void
SteamAppDAO::update(unsigned long i) {
g_main_gui->refresh_app_icon(i);
}

View File

@ -5,41 +5,53 @@
#include <ctime>
#include <sys/stat.h>
#include <dirent.h>
#include <curl/curl.h>
#include <fstream>
#include <sstream>
#include "../common/c_processes.h"
#include "../common/Downloader.h"
#include "globals.h"
class SteamAppDAO {
class SteamAppDAO : public Observer<unsigned long> {
public:
//TODO tidy
static SteamAppDAO* get_instance() {
static SteamAppDAO me;
return &me;
}
/**
* Redownloads http://api.steampowered.com/ISteamApps/GetAppList/v0002/
* if necessary. Redownloads every few days.
* TODO: Maybe pass a boolean too as argument for "Override redownload"
*/
static void update_name_database();
void update_name_database();
/**
* Feed it an appId, returns the app name.
* Make sure to call update_name_database at least once
* before using.
*/
static std::string get_app_name(const unsigned long& app_id);
std::string get_app_name(const unsigned long& app_id);
/**
* Download the app's banner ASYNCHRONOUSLY.
* If it fails, nothing is written.
*/
static void download_app_icon(const unsigned long& app_id);
void download_app_icon(const unsigned long& app_id);
/**
* Path name to the root of the cache folder. By now it is
* ~/.SamRewritten
*/
static const char *CACHE_FOLDER;
static const char* CACHE_FOLDER;
//TODO tidy
void update(unsigned long i);
SteamAppDAO(SteamAppDAO const&) = delete;
void operator=(SteamAppDAO const&) = delete;
private:
static void download_file(const std::string& file_url, const std::string& local_path);
SteamAppDAO() {Downloader::get_instance()->attach(this);};
~SteamAppDAO() {};
static void parse_app_names();
static std::map<unsigned long, std::string> m_app_names;

9
SAM.Picker/globals.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
class MySteam;
#include "MainPickerWindow.h"
//TODO tidy & comment
//Globals
//Reason for the globals is that it's easier to access them in GTK callbacks
extern MySteam *g_steam; // The Model
extern MainPickerWindow *g_main_gui; // The view

View File

@ -4,20 +4,17 @@
*/
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <gmodule.h>
#include "MySteam.h"
#include "MainPickerWindow.h"
#include "SteamAppDAO.h"
#include "globals.h"
int launcher_main();
//Globals
//Reason for the globals is that it's easier to access them in GTK callbacks
MySteam *g_steam = nullptr; // The Model
MainPickerWindow *g_main_gui = nullptr; // The view
MySteam* g_steam = nullptr;
MainPickerWindow* g_main_gui = nullptr;
int
main(int argc, char *argv[])

45
common/Downloader.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "Downloader.h"
void
Downloader::download_file(const std::string& file_url, const std::string& local_path, const unsigned long& dl_id) {
//If the file exists, there's no need to download it again.
//We assume the banners don't change
if(!file_exists(local_path)) {
CURL *curl;
FILE *fp;
CURLcode res;
curl = curl_easy_init();
if (curl) {
fp = fopen(local_path.c_str(),"wb");
curl_easy_setopt(curl, CURLOPT_URL, file_url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
else {
std::cerr << "An error occurred creating curl. Please report to the developers!" << std::endl;
exit(EXIT_FAILURE);
}
if(res != 0) {
std::cerr << "Curl returned with status " << res << ", which is unexpected. Make sure you are connected to the internet, and you have access";
std::cerr << " to Steam, and try again." << std::endl;
std::cerr << "Unable to fetch file " << file_url << std::endl;
//TODO make a gui for this
exit(EXIT_FAILURE);
}
}
notify(dl_id);
}
void
Downloader::download_file_async(const std::string& file_url, const std::string& local_path, const unsigned long& dl_id) {
//std::async(std::launch::async, &Downloader::download_file, this, file_url, local_path, dl_id);
std::async([this, file_url, local_path, dl_id]{this->download_file(file_url, local_path, dl_id);});
}

29
common/Downloader.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef DOWNLOADER
#define DOWNLOADER
#pragma once
#include "ObserverClasses.h"
#include "c_processes.h"
#include <curl/curl.h>
#include <string>
#include <future>
//TODO comment & tidy
class Downloader : public Subject<unsigned long> {
public:
static Downloader* get_instance() {
static Downloader me;
return &me;
};
void download_file(const std::string& file_url, const std::string& local_path, const unsigned long& dl_id);
void download_file_async(const std::string& file_url, const std::string& local_path, const unsigned long& dl_id);
Downloader(Downloader const&) = delete;
void operator=(Downloader const&) = delete;
private:
Downloader() {};
~Downloader() {};
};
#endif

32
common/ObserverClasses.h Normal file
View File

@ -0,0 +1,32 @@
#include <iostream>
#include <vector>
using namespace std;
/**
* This is a really limited observer pattern.
* It's not fully implemented on purpose. For now,
* Ther's no need for more advanced functions.
*/
template<typename T> class Observer;
template <typename T>
class Subject {
vector < class Observer<T> * > views;
public:
void attach(Observer<T> *obs) {
views.push_back(obs);
};
void notify(T usr_data) {
for(Observer<T>* obs : views)
obs->update(usr_data);
};
};
template <typename T>
class Observer {
public:
virtual void update(T) = 0;
};

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<!-- Generated with glade 3.20.3 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListBoxRow" id="game_entry">
@ -145,6 +145,18 @@
</packing>
</child>
</object>
<object class="GtkPopover" id="search_popover">
<property name="can_focus">False</property>
<child>
<object class="GtkSearchEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
</object>
</child>
</object>
<object class="GtkApplicationWindow" id="main_window">
<property name="width_request">800</property>
<property name="height_request">500</property>
@ -233,6 +245,26 @@
</child>
</object>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="popover">search_popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-find-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>

View File

@ -5,8 +5,8 @@ SCRIPTPATH=`dirname $SCRIPT`
#export LD_LIBRARY_PATH=$SCRIPTPATH/bin
g++ -g `pkg-config --cflags gtk+-3.0` -rdynamic -export-dynamic -Wall SAM.Picker/*.cpp common/*.cpp -L$SCRIPTPATH/bin -o $SCRIPTPATH/bin/samrewritten `pkg-config --libs gtk+-3.0` -lgmodule-2.0 -lsteam_api -lcurl \
g++ -g `pkg-config --cflags gtk+-3.0` -rdynamic -export-dynamic -pthread -Wall SAM.Picker/*.cpp common/*.cpp -L$SCRIPTPATH/bin -o $SCRIPTPATH/bin/samrewritten `pkg-config --libs gtk+-3.0` -lpthread -lgmodule-2.0 -lsteam_api -lcurl \
&& \
g++ -g SAM.Game/*.cpp common/*.cpp -lsteam_api -L$SCRIPTPATH/bin -o $SCRIPTPATH/bin/samgame \
g++ -g SAM.Game/*.cpp common/c_processes.cpp common/lodepng.cpp -lsteam_api -L$SCRIPTPATH/bin -o $SCRIPTPATH/bin/samgame \
&& \
./bin/launch.sh