Skip to content
This repository has been archived by the owner on Mar 10, 2019. It is now read-only.

Commit

Permalink
Beginnings of offline mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Ashby committed Feb 3, 2018
1 parent 294fbb7 commit 29b9226
Show file tree
Hide file tree
Showing 13 changed files with 752 additions and 75 deletions.
3 changes: 2 additions & 1 deletion .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
"/usr/include/x86_64-linux-gnu",
"/usr/include",
"${workspaceRoot}",
"/usr/include/glib-2.0"
"/usr/include/glib-2.0",
"/usr/include/gtk-3.0"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
Expand Down
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
"deezer-connect.h": "c",
"deezer-track.h": "c",
"json-glib.h": "c",
"deezer-offline.h": "c"
"deezer-offline.h": "c",
"rb-deezer-offline-source.h": "c",
"rb-deezer-offline-entry-type.h": "c",
"type_traits": "c",
"rb-debug.h": "c"
}
}
5 changes: 5 additions & 0 deletions data/org.gnome.rhythmbox.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,11 @@
<summary>The access token for Deezer API</summary>
<description>The token for accessing paid Deezer API functionality. Issued via OAUTH.</description>
</key>
<key name="cache-size-kb" type="u">
<default>50000</default>
<summary>The maximum cache size in KB Deezer will use</summary>
<description>Deezer caches tracks locally. This settings determines how much space it is allowed.</description>
</key>
</schema>

</schemalist>
13 changes: 11 additions & 2 deletions plugins/deezerpl/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ libdeezerpl_la_SOURCES = \
rb-deezer-entry-type.h \
rb-deezer-entry-type.c \
yuarel.c \
yuarel.h
yuarel.h \
rb-deezer-offline-source.h \
rb-deezer-offline-source.c \
rb-deezer-offline-entry-type.h \
rb-deezer-offline-entry-type.c

libdeezerpl_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
libdeezerpl_la_LIBTOOLFLAGS = --tag=disable-static
Expand Down Expand Up @@ -46,7 +50,12 @@ AM_CPPFLAGS = \
plugin_in_files = deezer.plugin.in
%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache

gtkbuilderdir = $(plugindatadir)
gtkbuilder_DATA = \
deezer-popup.ui

plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
EXTRA_DIST = $(plugin_in_files)
EXTRA_DIST = $(plugin_in_files) \
$(gtkbuilder_DATA)
CLEANFILES = $(plugin_DATA)
DISTCLEANFILES = $(plugin_DATA)
11 changes: 11 additions & 0 deletions plugins/deezerpl/deezer-popup.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<interface>
<menu id="deezer-popup">
<section>
<item>
<attribute name="label" translatable="yes">Save offline</attribute>
<attribute name="action">app.save-deezer-offline</attribute>
</item>
</section>
</menu>
</interface>
21 changes: 21 additions & 0 deletions plugins/deezerpl/rb-deezer-offline-entry-type.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "rb-deezer-offline-entry-type.h"

struct _RBDeezerOfflineEntryType {
RhythmDBEntryType parent;
};

static void rb_deezer_offline_entry_type_init(RBDeezerOfflineEntryType* entry_type) {}

static void rb_deezer_offline_entry_type_class_init(RBDeezerOfflineEntryTypeClass* entry_type_cls) {}

static void rb_deezer_offline_entry_type_class_finalize(RBDeezerOfflineEntryTypeClass* entry_type_cls) {}

G_DEFINE_DYNAMIC_TYPE(
RBDeezerOfflineEntryType,
rb_deezer_offline_entry_type,
rhythmdb_entry_type_get_type()
)

void _rb_deezer_offline_entry_type_register_type(GTypeModule* type_module) {
rb_deezer_offline_entry_type_register_type(type_module);
}
16 changes: 16 additions & 0 deletions plugins/deezerpl/rb-deezer-offline-entry-type.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef RB_OFFLINE_DEEZER_ENTRY_TYPE_H
#define RB_OFFLINE_DEEZER_ENTRY_TYPE_H

#include <glib-object.h>
#include "rhythmdb-entry-type.h"

G_DECLARE_FINAL_TYPE (
RBDeezerOfflineEntryType,
rb_deezer_offline_entry_type,
RB, DEEZER_OFFLINE_ENTRY_TYPE,
RhythmDBEntryType
);

void _rb_deezer_offline_entry_type_register_type(GTypeModule*);

#endif // RB_OFFLINE_DEEZER_ENTRY_TYPE_H
216 changes: 216 additions & 0 deletions plugins/deezerpl/rb-deezer-offline-source.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
#include "rb-deezer-offline-source.h"
#include "rb-deezer-plugin.h"
#include "rb-debug.h"
#include <deezer-offline.h>
#include <deezer-api.h>
#include <string.h>

static void rb_deezer_offline_source_state_cb(void* delegate,
void* operation_userdata,
dz_error_t error,
dz_object_handle result);

/**
* Offline source shows available cached offline tracks
*/
struct _RBDeezerOfflineSource {
RBBrowserSource parent;
};


struct _rb_deezer_offline_source_on_event_cb_args {
RBDeezerOfflineSource* src;
dz_offline_event_handle event;
};

static gboolean rb_deezer_offline_source_on_event_cb_main(gpointer data) {
struct _rb_deezer_offline_source_on_event_cb_args* args =
(struct _rb_deezer_offline_source_on_event_cb_args*)data;
// RBDeezerOfflineSource* src = args->src;
dz_offline_event_handle event = args->event;
dz_offline_event_t event_type = dz_offline_event_get_type(event);
switch (event_type) {
case DZ_OFFLINE_EVENT_UNKNOWN:
rb_debug("DZ_OFFLINE_EVENT_UNKNOWN");
break; /**< Offline event has not been set yet, not a valid value. */
case DZ_OFFLINE_EVENT_NEW_SYNC_STATE:
rb_debug("DZ_OFFLINE_EVENT_NEW_SYNC_STATE");
dz_offline_sync_state_t state = dz_offline_event_resource_get_sync_state(event);
const char* resource_id = dz_offline_event_get_resource_id(event);
const char* resource_type = dz_offline_event_get_resource_type(event);
const char* resource_state = dz_offline_state_to_cchar(state);
rb_debug("Resource [%s] Type [%s] State [%s]", resource_id, resource_type, resource_state);
break; /**< Send when the state on a resource has changed. */
case DZ_OFFLINE_EVENT_DOWNLOAD_PROGRESS:
rb_debug("DZ_OFFLINE_EVENT_DOWNLOAD_PROGRESS");
break; /**< Send progress event on a resource operation. */
}
DZ_OBJECT_RELEASE(event);
free(args);
return G_SOURCE_REMOVE;
}

static void rb_deezer_offline_source_on_event_cb(dz_connect_handle handle,
dz_offline_event_handle event,
void* delegate) {
RBDeezerOfflineSource* src = RB_DEEZER_OFFLINE_SOURCE(delegate);
struct _rb_deezer_offline_source_on_event_cb_args* args
= malloc(sizeof(struct _rb_deezer_offline_source_on_event_cb_args));
DZ_OBJECT_RETAIN(event);
args->event = event;
args->src = src;
gdk_threads_add_idle(rb_deezer_offline_source_on_event_cb_main, args);
}

struct _rb_deezer_offline_source_state_cb_args {
RBDeezerOfflineSource* src;
dz_object_handle result;
};

// static void rb_deezer_offline_source_log_data_cb(void* delegate,
// void* operation_userdata,
// dz_error_t error,
// dz_object_handle result) {
// const char* resource_json = dz_offline_resource_to_json(result);
// GError* err;
// JsonNode* node = json_from_string(resource_json, &err);
// rb_debug("resource:\n%s", json_to_string(node, true) );
// }

static void rb_deezer_offline_api_request_cb(dz_api_request_processing_handle handle,
dz_api_result_t ret,
dz_stream_object responsedata,
void* userdata
) {
// const char* resource_json = dz_offline_resource_to_json(result);
// dz_player_event_track_selected_dzapiinfo
GError* err;
JsonNode* node = json_from_string((const char*)responsedata, &err);
rb_debug("API result:\n%s", json_to_string(node, true) );
}

static void rb_deezer_offline_source_track_data_cb(void* delegate,
void* operation_userdata,
dz_error_t error,
dz_object_handle result) {
const char* resource_json = dz_offline_resource_to_json(result);
GError* err;
JsonNode* node = json_from_string(resource_json, &err);
rb_debug("resource:\n%s", json_to_string(node, true) );

RBDeezerOfflineSource* src = RB_DEEZER_OFFLINE_SOURCE(operation_userdata);
RBDeezerPlugin* plugin;
g_object_get(src, "plugin", &plugin, NULL);

JsonObject* obj = json_node_get_object(node);
JsonArray* arr = json_object_get_array_member(obj, "tracks");
for (guint i=0; i<json_array_get_length(arr); i++) {
int track_id = json_array_get_int_element(arr, i);
char buf[100];
// snprintf(buf, 100, "/dzlib/track_offline/%d", track_id);
snprintf(buf, 100, "/track/%d", track_id);
// DZ(dz_offline_get, plugin->handle, rb_deezer_offline_source_log_data_cb, src, buf);
dz_api_request_handle hnd = dz_api_request_new(DZ_API_CMD_GET, buf);
// dz_api_request_handle req_hnd =
dz_api_request_processing_async(plugin->handle, hnd, NULL, rb_deezer_offline_api_request_cb, src);
}
g_object_unref(plugin);
}

static gboolean rb_deezer_offline_source_state_cb_main(gpointer data) {
struct _rb_deezer_offline_source_state_cb_args* args
= (struct _rb_deezer_offline_source_state_cb_args*)data;
RBDeezerOfflineSource* src = args->src;
dz_object_handle result = args->result;

RBDeezerPlugin* plugin;
g_object_get(src, "plugin", &plugin, NULL);

const char* offline_state_json = dz_offline_state_to_json(result);
GError* err;
JsonNode* node = json_from_string(offline_state_json, &err);
rb_debug("Offline state: \n%s", json_to_string(node, true));

JsonArray* offline_objs = json_node_get_array(node);
for (guint i=0; i<json_array_get_length(offline_objs); i++) {
JsonObject* offline_obj = json_array_get_object_element(offline_objs, i);
const char* state = json_object_get_string_member(offline_obj, "state");
const char* path = json_object_get_string_member(offline_obj, "resource_id");
bool is_tracklist = g_str_has_prefix(path, "/dzlocal/tracklist/");
bool is_synced = strcmp(state, "synced") == 0;
if (is_tracklist && is_synced) {
DZ(dz_offline_get, plugin->handle, rb_deezer_offline_source_track_data_cb, src, path);
}
}

g_object_unref(plugin);
free(args);
DZ_OBJECT_RELEASE(result);
return G_SOURCE_REMOVE;
}

static void rb_deezer_offline_source_state_cb(void* delegate,
void* operation_userdata,
dz_error_t error,
dz_object_handle result) {
if (error != DZ_ERROR_NO_ERROR) {
rb_debug("Eeror getting offline state %s", dz_error_string(error));
return;
}
DZ_OBJECT_RETAIN(result);
struct _rb_deezer_offline_source_state_cb_args* args
= malloc(sizeof(struct _rb_deezer_offline_source_state_cb_args));
args->src = RB_DEEZER_OFFLINE_SOURCE(operation_userdata);
args->result = result;
gdk_threads_add_idle(
rb_deezer_offline_source_state_cb_main,
args
);
}

static void rb_deezer_offline_source_init(RBDeezerOfflineSource* src) {}

static void rb_deezer_offline_constructed(GObject* object) {
RBDeezerPlugin* plugin;
g_object_get(object, "plugin", &plugin, NULL);

// Set ourselves up as offline event listener
dz_error_t err = dz_offline_eventcb_set(plugin->handle,
NULL,
RB_DEEZER_OFFLINE_SOURCE(object),
rb_deezer_offline_source_on_event_cb
);
if (err != DZ_ERROR_NO_ERROR) {
rb_debug("Error registering offline callback %s", dz_error_string(err));
}

// Query initial state
err = dz_offline_get_state(plugin->handle,
rb_deezer_offline_source_state_cb,
RB_DEEZER_OFFLINE_SOURCE(object),
"/dzlib/tracklists",
true
);
if (err != DZ_ERROR_NO_ERROR) {
rb_debug("Error requesting offline content %s", dz_error_string(err));
}
g_object_unref(plugin);
}

static void rb_deezer_offline_source_class_init(RBDeezerOfflineSourceClass* cls) {
GObjectClass* object_cls = G_OBJECT_CLASS(cls);
object_cls->constructed = rb_deezer_offline_constructed;
}

static void rb_deezer_offline_source_class_finalize(RBDeezerOfflineSourceClass* cls) {}

G_DEFINE_DYNAMIC_TYPE(
RBDeezerOfflineSource,
rb_deezer_offline_source,
RB_TYPE_BROWSER_SOURCE
);

// Required for PEAS plugin system
void _rb_deezer_offline_source_register_type(GTypeModule* type_module) {
rb_deezer_offline_source_register_type(type_module);
}
26 changes: 26 additions & 0 deletions plugins/deezerpl/rb-deezer-offline-source.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef DEEZER_OFFLINE_SOURCE_H
#define DEEZER_OFFLINE_SOURCE_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib-object.h>
#include "rb-browser-source.h"

G_BEGIN_DECLS

typedef struct _RBDeezerPlugin RBDeezerPlugin;

G_DECLARE_FINAL_TYPE(
RBDeezerOfflineSource,
rb_deezer_offline_source,
RB, DEEZER_OFFLINE_SOURCE,
RBBrowserSource
);

void _rb_deezer_offline_source_register_type (GTypeModule *type_module);

G_END_DECLS

#endif // DEEZER_OFFLINE_SOURCE_H
Loading

0 comments on commit 29b9226

Please sign in to comment.