mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-18 04:19:55 +01:00
Add Seafile Support
This commit is contained in:
parent
162b2efbb0
commit
1fc95cb7ed
@ -203,6 +203,11 @@ optional_component(BOX ON "Box support"
|
||||
|
||||
optional_component(VK ON "Vk.com support")
|
||||
|
||||
optional_component(SEAFILE ON "Seafile support"
|
||||
DEPENDS "Google sparsehash" SPARSEHASH_INCLUDE_DIRS
|
||||
DEPENDS "Taglib 1.8" "TAGLIB_VERSION VERSION_GREATER 1.7.999"
|
||||
)
|
||||
|
||||
optional_component(AUDIOCD ON "Devices: Audio CD support"
|
||||
DEPENDS "libcdio" CDIO_FOUND
|
||||
)
|
||||
|
@ -386,6 +386,7 @@
|
||||
<file>schema/schema-44.sql</file>
|
||||
<file>schema/schema-45.sql</file>
|
||||
<file>schema/schema-46.sql</file>
|
||||
<file>schema/schema-47.sql</file>
|
||||
<file>schema/schema-4.sql</file>
|
||||
<file>schema/schema-5.sql</file>
|
||||
<file>schema/schema-6.sql</file>
|
||||
@ -424,5 +425,6 @@
|
||||
<file>vk/deactivated.gif</file>
|
||||
<file>providers/vk.png</file>
|
||||
<file>vk/link.png</file>
|
||||
<file>providers/seafile.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
BIN
data/providers/seafile.png
Normal file
BIN
data/providers/seafile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
@ -1,24 +1,48 @@
|
||||
CREATE VIRTUAL TABLE playlist_items_fts USING fts3(
|
||||
CREATE TABLE seafile_songs(
|
||||
title TEXT,
|
||||
album TEXT,
|
||||
artist TEXT,
|
||||
albumartist TEXT,
|
||||
composer TEXT,
|
||||
track INTEGER,
|
||||
disc INTEGER,
|
||||
bpm REAL,
|
||||
year INTEGER,
|
||||
genre TEXT,
|
||||
comment TEXT,
|
||||
compilation INTEGER,
|
||||
|
||||
length INTEGER,
|
||||
bitrate INTEGER,
|
||||
samplerate INTEGER,
|
||||
|
||||
directory INTEGER NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
mtime INTEGER NOT NULL,
|
||||
ctime INTEGER NOT NULL,
|
||||
filesize INTEGER NOT NULL,
|
||||
sampler INTEGER NOT NULL DEFAULT 0,
|
||||
art_automatic TEXT,
|
||||
art_manual TEXT,
|
||||
filetype INTEGER NOT NULL DEFAULT 0,
|
||||
playcount INTEGER NOT NULL DEFAULT 0,
|
||||
lastplayed INTEGER,
|
||||
rating INTEGER,
|
||||
forced_compilation_on INTEGER NOT NULL DEFAULT 0,
|
||||
forced_compilation_off INTEGER NOT NULL DEFAULT 0,
|
||||
effective_compilation NOT NULL DEFAULT 0,
|
||||
skipcount INTEGER NOT NULL DEFAULT 0,
|
||||
score INTEGER NOT NULL DEFAULT 0,
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
cue_path TEXT,
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
effective_albumartist TEXT,
|
||||
etag TEXT
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE seafile_songs_fts USING fts3 (
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
DELETE FROM %allsongstables_fts;
|
||||
|
||||
DROP TABLE %allsongstables_fts;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN performer TEXT;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN grouping TEXT;
|
||||
|
||||
CREATE VIRTUAL TABLE %allsongstables_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
INSERT INTO %allsongstables_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
|
||||
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment
|
||||
FROM %allsongstables;
|
||||
|
||||
UPDATE schema_version SET version=45;
|
||||
|
||||
|
@ -1,3 +1,24 @@
|
||||
ALTER TABLE playlists ADD COLUMN is_favorite INTEGER NOT NULL DEFAULT 0;
|
||||
CREATE VIRTUAL TABLE playlist_items_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
DELETE FROM %allsongstables_fts;
|
||||
|
||||
DROP TABLE %allsongstables_fts;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN performer TEXT;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN grouping TEXT;
|
||||
|
||||
CREATE VIRTUAL TABLE %allsongstables_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
INSERT INTO %allsongstables_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
|
||||
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment
|
||||
FROM %allsongstables;
|
||||
|
||||
UPDATE schema_version SET version=46;
|
||||
|
||||
|
3
data/schema/schema-47.sql
Normal file
3
data/schema/schema-47.sql
Normal file
@ -0,0 +1,3 @@
|
||||
ALTER TABLE playlists ADD COLUMN is_favorite INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
UPDATE schema_version SET version=47;
|
@ -1155,6 +1155,23 @@ optional_source(HAVE_VK
|
||||
internet/vksettingspage.ui
|
||||
)
|
||||
|
||||
# Seafile support
|
||||
optional_source(HAVE_SEAFILE
|
||||
SOURCES
|
||||
internet/seafileservice.cpp
|
||||
internet/seafilesettingspage.cpp
|
||||
internet/seafileurlhandler.cpp
|
||||
internet/seafiletree.cpp
|
||||
HEADERS
|
||||
internet/seafileservice.h
|
||||
internet/seafilesettingspage.h
|
||||
internet/seafileurlhandler.h
|
||||
internet/seafiletree.h
|
||||
UI
|
||||
internet/seafilesettingspage.ui
|
||||
)
|
||||
|
||||
|
||||
# Pulse audio integration
|
||||
optional_source(HAVE_LIBPULSE
|
||||
INCLUDE_DIRECTORIES
|
||||
|
@ -40,6 +40,7 @@
|
||||
#cmakedefine HAVE_SPARKLE
|
||||
#cmakedefine HAVE_SPOTIFY_DOWNLOADER
|
||||
#cmakedefine HAVE_VK
|
||||
#cmakedefine HAVE_SEAFILE
|
||||
#cmakedefine HAVE_WIIMOTEDEV
|
||||
#cmakedefine TAGLIB_HAS_OPUS
|
||||
#cmakedefine USE_INSTALL_PREFIX
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <QVariant>
|
||||
|
||||
const char* Database::kDatabaseFilename = "clementine.db";
|
||||
const int Database::kSchemaVersion = 46;
|
||||
const int Database::kSchemaVersion = 47;
|
||||
const char* Database::kMagicAllSongsTables = "%allsongstables";
|
||||
|
||||
int Database::sNextConnectionId = 1;
|
||||
|
@ -53,6 +53,10 @@
|
||||
#ifdef HAVE_VK
|
||||
#include "vkservice.h"
|
||||
#endif
|
||||
#ifdef HAVE_SEAFILE
|
||||
#include "seafileservice.h"
|
||||
#endif
|
||||
|
||||
|
||||
using smart_playlists::Generator;
|
||||
using smart_playlists::GeneratorMimeData;
|
||||
@ -101,6 +105,9 @@ InternetModel::InternetModel(Application* app, QObject* parent)
|
||||
#ifdef HAVE_VK
|
||||
AddService(new VkService(app, this));
|
||||
#endif
|
||||
#ifdef HAVE_SEAFILE
|
||||
AddService(new SeafileService(app, this));
|
||||
#endif
|
||||
|
||||
invisibleRootItem()->sortChildren(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
568
src/internet/seafileservice.cpp
Normal file
568
src/internet/seafileservice.cpp
Normal file
@ -0,0 +1,568 @@
|
||||
#include "seafileservice.h"
|
||||
|
||||
#include <qjson/parser.h>
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/player.h"
|
||||
#include "core/waitforsignal.h"
|
||||
#include "internet/seafileurlhandler.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "internet/oauthenticator.h"
|
||||
|
||||
|
||||
const char* SeafileService::kServiceName = "Seafile";
|
||||
const char* SeafileService::kSettingsGroup = "Seafile";
|
||||
|
||||
namespace {
|
||||
|
||||
static const char* kAuthToken = "/api2/auth-token/";
|
||||
|
||||
static const char* kFolderItems = "/api2/repos/%1/dir/";
|
||||
static const char* kListRepos = "/api2/repos/";
|
||||
|
||||
static const char* kFileUrl = "/api2/repos/%1/file/";
|
||||
static const char* kFileContent = "/api2/repos/%1/file/detail/";
|
||||
}
|
||||
|
||||
SeafileService::SeafileService(Application* app, InternetModel* parent)
|
||||
: CloudFileService(app, parent, kServiceName, kSettingsGroup,
|
||||
QIcon(":/providers/seafile.png"), SettingsDialog::Page_Seafile) {
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
access_token_ = s.value("access_token").toString();
|
||||
server_ = s.value("server").toString();
|
||||
|
||||
QByteArray tree_bytes = s.value("tree").toByteArray();
|
||||
|
||||
if(!tree_bytes.isEmpty()) {
|
||||
QDataStream stream(&tree_bytes, QIODevice::ReadOnly);
|
||||
stream >> tree_;
|
||||
}
|
||||
|
||||
app->player()->RegisterUrlHandler(new SeafileUrlHandler(this, this));
|
||||
|
||||
connect(&tree_, SIGNAL(ToAdd(QString, QString, SeafileTree::Entry)), this, SLOT(AddEntry(QString, QString, SeafileTree::Entry)));
|
||||
connect(&tree_, SIGNAL(ToDelete(QString, QString, SeafileTree::Entry)), this, SLOT(DeleteEntry(QString, QString, SeafileTree::Entry)));
|
||||
connect(&tree_, SIGNAL(ToUpdate(QString, QString, SeafileTree::Entry)), this, SLOT(UpdateEntry(QString, QString, SeafileTree::Entry)));
|
||||
|
||||
library_updated_ = QString::null;
|
||||
|
||||
}
|
||||
|
||||
bool SeafileService::has_credentials() const {
|
||||
return !access_token().isEmpty();
|
||||
}
|
||||
|
||||
bool SeafileService::is_authenticated() const {
|
||||
return !access_token_.isEmpty();
|
||||
}
|
||||
|
||||
QString SeafileService::access_token() const {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
return s.value("access_token").toString();
|
||||
}
|
||||
|
||||
void SeafileService::AddAuthorizationHeader(QNetworkRequest* request) const {
|
||||
request->setRawHeader("Authorization", QString(QString("Token ") + QString(access_token_)).toAscii());
|
||||
}
|
||||
|
||||
void SeafileService::ForgetCredentials() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
s.remove("access_token");
|
||||
s.remove("tree");
|
||||
access_token_.clear();
|
||||
tree_.Clear();
|
||||
|
||||
server_.clear();
|
||||
}
|
||||
|
||||
bool SeafileService::GetToken(const QString &mail, const QString &password, const QString &server) {
|
||||
QUrl url(server + kAuthToken);
|
||||
QNetworkRequest request(url);
|
||||
AddAuthorizationHeader(&request);
|
||||
|
||||
url.addQueryItem("username", mail);
|
||||
url.addQueryItem("password", password);
|
||||
|
||||
QNetworkReply* reply = network_->post(request, url.encodedQuery());
|
||||
reply->ignoreSslErrors();
|
||||
WaitForSignal(reply, SIGNAL(finished()));
|
||||
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (GetToken)";
|
||||
return false;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
QJson::Parser parser;
|
||||
QVariantMap response = parser.parse(reply->readAll()).toMap();
|
||||
|
||||
// Because the server responds "token"
|
||||
access_token_ = response["token"].toString().replace("\"", "");
|
||||
|
||||
if(access_token_.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("access_token", access_token_);
|
||||
|
||||
server_ = server;
|
||||
|
||||
emit Connected();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SeafileService::GetLibraries() {
|
||||
QUrl url(server_ + kListRepos);
|
||||
QNetworkRequest request(url);
|
||||
AddAuthorizationHeader(&request);
|
||||
QNetworkReply *reply = network_->get(request);
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(GetLibrariesFinished(QNetworkReply*)), reply);
|
||||
}
|
||||
|
||||
void SeafileService::GetLibrariesFinished(QNetworkReply *reply) {
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (GetLibraries)";
|
||||
return;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
// key : id, value : name
|
||||
QMap<QString, QString> libraries;
|
||||
QByteArray data = reply->readAll();
|
||||
QJson::Parser parser;
|
||||
QList<QVariant> repos = parser.parse(data).toList();
|
||||
|
||||
for (int i=0; i<repos.size(); ++i) {
|
||||
QVariantMap repo = repos.at(i).toMap();
|
||||
QString repo_name = repo["name"].toString(),
|
||||
repo_id = repo["id"].toString();
|
||||
|
||||
// One library can appear several times and we don't add encrypted libraries (not supported yet)
|
||||
if(!libraries.contains(repo_id) && !repo["encrypted"].toBool()) {
|
||||
libraries.insert(repo_id, repo_name);
|
||||
}
|
||||
}
|
||||
|
||||
emit GetLibrariesFinishedSignal(libraries);
|
||||
}
|
||||
|
||||
|
||||
void SeafileService::ChangeLibrary(const QString &new_library) {
|
||||
|
||||
// Every other libraries have to be destroyed from the tree
|
||||
if(new_library != "all") {
|
||||
|
||||
for (SeafileTree::TreeItem *library : tree_.libraries()) {
|
||||
if (new_library != library->entry().id()) {
|
||||
DeleteEntry(library->entry().id(), "/", library->entry());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UpdateLibraries();
|
||||
}
|
||||
|
||||
void SeafileService::Connect() {
|
||||
if (is_authenticated()) {
|
||||
UpdateLibraries();
|
||||
}
|
||||
else {
|
||||
ShowSettingsDialog();
|
||||
}
|
||||
}
|
||||
|
||||
void SeafileService::UpdateLibraries() {
|
||||
|
||||
connect(this, SIGNAL(GetLibrariesFinishedSignal(QMap<QString,QString>)), this,
|
||||
SLOT(UpdateLibrariesInProgress(QMap<QString,QString>)));
|
||||
|
||||
GetLibraries();
|
||||
}
|
||||
|
||||
void SeafileService::UpdateLibrariesInProgress(const QMap<QString, QString> &libraries) {
|
||||
disconnect(this, SIGNAL(GetLibrariesFinishedSignal(QMap<QString,QString>)), this,
|
||||
SLOT(UpdateLibrariesInProgress(QMap<QString,QString>)));
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
QString library_to_update = s.value("library").toString();
|
||||
|
||||
// If the library doesn't change, we don't need to update
|
||||
if (!library_updated_.isNull() && library_updated_ == library_to_update) {
|
||||
return;
|
||||
}
|
||||
|
||||
library_updated_ = library_to_update;
|
||||
|
||||
if(library_to_update == "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
QMapIterator<QString, QString> library(libraries);
|
||||
while (library.hasNext()) {
|
||||
library.next();
|
||||
|
||||
// Need to check this library ?
|
||||
if (library_to_update == "all" || library.key() == library_to_update) {
|
||||
|
||||
FetchAndCheckFolderItems(
|
||||
SeafileTree::Entry(library.value(), library.key(), SeafileTree::Entry::LIBRARY), "/");
|
||||
}
|
||||
// If not, we can destroy the library from the tree
|
||||
else {
|
||||
// If the library was not in the tree, it's not a problem because DeleteEntry won't do anything
|
||||
DeleteEntry(library.key(), "/",
|
||||
SeafileTree::Entry(library.value(), library.key(), SeafileTree::Entry::LIBRARY));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QNetworkReply* SeafileService::PrepareFetchFolderItems(const QString &library, const QString &path) {
|
||||
QUrl url(server_ + QString(kFolderItems).arg(library));
|
||||
url.addQueryItem("p", path);
|
||||
|
||||
QNetworkRequest request(url);
|
||||
AddAuthorizationHeader(&request);
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void SeafileService::FetchAndCheckFolderItems(const SeafileTree::Entry &library, const QString &path) {
|
||||
QNetworkReply *reply = PrepareFetchFolderItems(library.id(), path);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(FetchAndCheckFolderItemsFinished(QNetworkReply*,SeafileTree::Entry,QString)),
|
||||
reply, library, path);
|
||||
}
|
||||
|
||||
void SeafileService::FetchAndCheckFolderItemsFinished(
|
||||
QNetworkReply *reply, const SeafileTree::Entry &library, const QString &path) {
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (FetchFolderItemsToList)";
|
||||
return;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
QJson::Parser parser;
|
||||
QList<QVariant> variant_entries = parser.parse(data).toList();
|
||||
|
||||
SeafileTree::Entries entries;
|
||||
for (const QVariant & e: variant_entries) {
|
||||
QVariantMap entry = e.toMap();
|
||||
SeafileTree::Entry::Type entry_type = SeafileTree::Entry::StringToType(entry["type"].toString());
|
||||
QString entry_name = entry["name"].toString();
|
||||
|
||||
// We just want libraries/directories and files which could be songs.
|
||||
if (entry_type == SeafileTree::Entry::NONE) {
|
||||
qLog(Warning) << "Type entry unknown for this entry";
|
||||
}
|
||||
else if (entry_type == SeafileTree::Entry::FILE && GuessMimeTypeForFile(entry_name).isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.append(SeafileTree::Entry(entry_name, entry["id"].toString(), entry_type));
|
||||
}
|
||||
|
||||
tree_.CheckEntries(entries, library, path);
|
||||
}
|
||||
|
||||
|
||||
void SeafileService::AddRecursivelyFolderItems(const QString &library, const QString &path) {
|
||||
QNetworkReply *reply = PrepareFetchFolderItems(library, path);
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(AddRecursivelyFolderItemsFinished(QNetworkReply*,QString,QString)), reply, library, path);
|
||||
}
|
||||
|
||||
void SeafileService::AddRecursivelyFolderItemsFinished(QNetworkReply* reply, const QString &library, const QString &path) {
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (FetchFolderItems)";
|
||||
return;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QJson::Parser parser;
|
||||
QList<QVariant> entries = parser.parse(data).toList();
|
||||
|
||||
for (const QVariant& e : entries) {
|
||||
QVariantMap entry_map = e.toMap();
|
||||
SeafileTree::Entry::Type entry_type = SeafileTree::Entry::StringToType(entry_map["type"].toString());
|
||||
QString entry_name = entry_map["name"].toString();
|
||||
|
||||
// We just want libraries/directories and files which could be songs.
|
||||
if (entry_type == SeafileTree::Entry::NONE) {
|
||||
qLog(Warning) << "Type entry unknown for this entry";
|
||||
}
|
||||
else if (entry_type == SeafileTree::Entry::FILE && GuessMimeTypeForFile(entry_name).isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SeafileTree::Entry entry(entry_name, entry_map["id"].toString(), entry_type);
|
||||
|
||||
// If AddEntry was not successful we stop
|
||||
// It could happen when the user changes the library to update while an update was in progress
|
||||
if(!tree_.AddEntry(library, path, entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.is_dir()) {
|
||||
AddRecursivelyFolderItems(library, path + entry.name() + "/");
|
||||
}
|
||||
else {
|
||||
MaybeAddFileEntry(entry.name(), library, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReply *SeafileService::PrepareFetchContentForFile(const QString &library, const QString &filepath) {
|
||||
QUrl content_url(server_ + QString(kFileContent).arg(library));
|
||||
content_url.addQueryItem("p", filepath);
|
||||
|
||||
QNetworkRequest request(content_url);
|
||||
AddAuthorizationHeader(&request);
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
void SeafileService::MaybeAddFileEntry(const QString &entry_name, const QString &library, const QString &path) {
|
||||
QString mime_type = GuessMimeTypeForFile(entry_name);
|
||||
|
||||
if(mime_type.isNull())
|
||||
return;
|
||||
|
||||
// Get the details of the entry
|
||||
QNetworkReply *reply = PrepareFetchContentForFile(library, path + entry_name);
|
||||
NewClosure(reply, SIGNAL(finished()), this,
|
||||
SLOT(MaybeAddFileEntryInProgress(QNetworkReply*,QString,QString,QString)),
|
||||
reply, library, path, mime_type);
|
||||
}
|
||||
|
||||
void SeafileService::MaybeAddFileEntryInProgress(
|
||||
QNetworkReply *reply, const QString &library, const QString &path, const QString &mime_type) {
|
||||
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (MaybeAddFileEntry)";
|
||||
return;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
QJson::Parser parser;
|
||||
QVariantMap entry_detail_map = parser.parse(data).toMap();
|
||||
|
||||
QUrl url;
|
||||
url.setScheme("seafile");
|
||||
url.setPath("/" + library + path + entry_detail_map["name"].toString());
|
||||
|
||||
Song song;
|
||||
song.set_url(url);
|
||||
song.set_ctime(0);
|
||||
song.set_mtime(entry_detail_map["mtime"].toInt());
|
||||
song.set_filesize(entry_detail_map["size"].toInt());
|
||||
song.set_title(entry_detail_map["name"].toString());
|
||||
|
||||
// Get the download url of the entry
|
||||
reply = PrepareFetchContentUrlForFile(library, path + entry_detail_map["name"].toString());
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(FetchContentUrlForFileFinished(QNetworkReply*, Song, QString)), reply, song, mime_type);
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply* SeafileService::PrepareFetchContentUrlForFile(const QString &library, const QString &filepath) {
|
||||
QUrl content_url(server_ + QString(kFileUrl).arg(library));
|
||||
content_url.addQueryItem("p", filepath);
|
||||
|
||||
QNetworkRequest request(content_url);
|
||||
AddAuthorizationHeader(&request);
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void SeafileService::FetchContentUrlForFileFinished(QNetworkReply* reply, const Song &song, const QString &mime_type) {
|
||||
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (FetchContentUrlForFile)";
|
||||
return;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
// Because server response is "http://..."
|
||||
QString real_url = QString(reply->readAll()).replace("\"", "");
|
||||
|
||||
MaybeAddFileToDatabase(song, mime_type, QUrl(real_url), QString("Token %1").arg(access_token_));
|
||||
}
|
||||
|
||||
QUrl SeafileService::GetStreamingUrlFromSongId(const QString &library, const QString &filepath) {
|
||||
|
||||
QNetworkReply* reply = PrepareFetchContentUrlForFile(library, filepath);
|
||||
reply->ignoreSslErrors();
|
||||
WaitForSignal(reply, SIGNAL(finished()));
|
||||
|
||||
if(!CheckReply(&reply)) {
|
||||
qLog(Warning) << "Something wrong with the reply... (GetStreamingUrlFromSongId)";
|
||||
return QUrl("");
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
QString response = QString(reply->readAll()).replace("\"", "");
|
||||
|
||||
return QUrl(response);
|
||||
}
|
||||
|
||||
|
||||
void SeafileService::AddEntry(const QString &library, const QString &path, const SeafileTree::Entry &entry) {
|
||||
|
||||
if (entry.is_library()) {
|
||||
tree_.AddLibrary(entry.name(), entry.id());
|
||||
AddRecursivelyFolderItems(library, "/");
|
||||
}
|
||||
else {
|
||||
// If AddEntry was not successful we stop
|
||||
// It could happen when the user changes the library to update while an update was in progress
|
||||
if(!tree_.AddEntry(library, path, entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.is_file()) {
|
||||
MaybeAddFileEntry(entry.name(), library, path);
|
||||
}
|
||||
else {
|
||||
AddRecursivelyFolderItems(library, path + entry.name() + "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SeafileService::UpdateEntry(const QString & library, const QString &path, const SeafileTree::Entry &entry) {
|
||||
|
||||
if (entry.is_file()) {
|
||||
DeleteEntry(library, path, entry);
|
||||
AddEntry(library, path, entry);
|
||||
}
|
||||
else {
|
||||
QString entry_path = path;
|
||||
|
||||
if(entry.is_dir()) {
|
||||
entry_path += entry.name() + "/";
|
||||
}
|
||||
|
||||
FetchAndCheckFolderItems(SeafileTree::Entry("", library, SeafileTree::Entry::LIBRARY),
|
||||
entry_path);
|
||||
}
|
||||
}
|
||||
|
||||
void SeafileService::DeleteEntry(const QString &library, const QString &path, const SeafileTree::Entry &entry) {
|
||||
|
||||
// For the QPair -> 1 : path, 2 : entry
|
||||
QList<QPair<QString, SeafileTree::Entry>> files_to_delete;
|
||||
if(entry.is_library()) {
|
||||
SeafileTree::TreeItem *item = tree_.FindLibrary(library);
|
||||
files_to_delete = tree_.GetRecursiveFilesOfDir("/", item);
|
||||
tree_.DeleteLibrary(library);
|
||||
}
|
||||
else {
|
||||
if(entry.is_dir()) {
|
||||
SeafileTree::TreeItem *item = tree_.FindFromAbsolutePath(library, path + entry.name() + "/");
|
||||
files_to_delete = tree_.GetRecursiveFilesOfDir(path + entry.name() + "/", item);
|
||||
}
|
||||
else {
|
||||
files_to_delete.append(qMakePair(path, entry));
|
||||
}
|
||||
|
||||
if(!tree_.DeleteEntry(library, path, entry)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete songs from the library of Clementine
|
||||
for (const QPair<QString, SeafileTree::Entry> &file_to_delete : files_to_delete) {
|
||||
if(!GuessMimeTypeForFile(file_to_delete.second.name())
|
||||
.isEmpty()) {
|
||||
QUrl song_url("seafile:/" + library + file_to_delete.first + file_to_delete.second.name());
|
||||
Song song = library_backend_->GetSongByUrl(song_url);
|
||||
|
||||
if (song.is_valid()) {
|
||||
library_backend_->DeleteSongs(SongList() << song);
|
||||
}
|
||||
else {
|
||||
qLog(Warning) << "Can't delete song from the Clementine's library : " << song_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SeafileService::CheckReply(QNetworkReply **reply) {
|
||||
if (!(*reply)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant status_code_variant = (*reply)->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
if (status_code_variant.isValid()) {
|
||||
int status_code = status_code_variant.toInt();
|
||||
|
||||
if (status_code == NO_ERROR) {
|
||||
return true;
|
||||
}
|
||||
else if (status_code == TOO_MANY_REQUESTS) {
|
||||
qLog(Debug) << "Too many requests, wait...";
|
||||
|
||||
// If there are too many requests, we just wait
|
||||
QTimer timer;
|
||||
timer.start(10000);
|
||||
WaitForSignal(&timer, SIGNAL(timeout()));
|
||||
|
||||
(*reply)->deleteLater();
|
||||
|
||||
// And we execute the reply again
|
||||
*reply = network_->get((*reply)->request());
|
||||
WaitForSignal(*reply, SIGNAL(finished()));
|
||||
|
||||
return CheckReply(reply);
|
||||
}
|
||||
}
|
||||
|
||||
qLog(Warning) << "Error for reply : " << status_code_variant.toInt();
|
||||
// Unknown, 404 ...
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
SeafileService::~SeafileService() {
|
||||
// Save the tree !
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
QByteArray tree_byte;
|
||||
QDataStream stream(&tree_byte, QIODevice::WriteOnly);
|
||||
stream << tree_;
|
||||
|
||||
s.setValue("tree", tree_byte);
|
||||
|
||||
}
|
||||
|
117
src/internet/seafileservice.h
Normal file
117
src/internet/seafileservice.h
Normal file
@ -0,0 +1,117 @@
|
||||
/* Contacts (for explanations, congratulations, insults) :
|
||||
* - <florian.bigard@gmail.com>
|
||||
*
|
||||
* Help :
|
||||
* - The "path" variable has to end with "/".
|
||||
* If we want to specify a filepath, the name of the variable has to be... filepath :)
|
||||
* - Seafile stores files in libraries (or repositories) so variable with the name "library" corresponds to the
|
||||
* Seafile library, not to the Clementine library
|
||||
* - The authentification of Seafile's API is simply a token (REST API)
|
||||
* - Seafile stores a hash for each entry. This hash changes when the entry is modified.
|
||||
* This is the reason why we just have to compare the local hash with the server
|
||||
* hash of a directory (for example) to know if the directory was modified.
|
||||
* Libraries are an exception : Seafile stores a hash that never changes.
|
||||
* This hash is called "id".
|
||||
*
|
||||
* Todo :
|
||||
* - Add ssl certificate exception (for people who generate their own certificate on their Seafile server
|
||||
* - Stop Tagreader when user changes the library
|
||||
*/
|
||||
|
||||
#ifndef SEAFILESERVICE_H
|
||||
#define SEAFILESERVICE_H
|
||||
|
||||
#include "cloudfileservice.h"
|
||||
#include "seafiletree.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMutex>
|
||||
|
||||
class QNetworkReply;
|
||||
class QNetworkRequest;
|
||||
|
||||
// Interface between the seafile server and Clementine
|
||||
class SeafileService : public CloudFileService {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
enum ApiError {
|
||||
NO_ERROR = 200,
|
||||
NOT_FOUND = 404,
|
||||
TOO_MANY_REQUESTS = 429
|
||||
};
|
||||
|
||||
SeafileService(Application* app, InternetModel* parent);
|
||||
~SeafileService();
|
||||
|
||||
static const char* kServiceName;
|
||||
static const char* kSettingsGroup;
|
||||
|
||||
virtual bool has_credentials() const;
|
||||
QUrl GetStreamingUrlFromSongId(const QString &library, const QString &filepath);
|
||||
// Get the token for an user (simple rest api)
|
||||
bool GetToken(const QString &mail, const QString &password, const QString &server);
|
||||
// Get all the libraries available for the user. Will emit a signal
|
||||
void GetLibraries();
|
||||
void ChangeLibrary(const QString &new_library);
|
||||
|
||||
public slots:
|
||||
void Connect();
|
||||
void ForgetCredentials();
|
||||
|
||||
signals:
|
||||
void Connected();
|
||||
// QMap, key : library's id, value : library's name
|
||||
void GetLibrariesFinishedSignal(QMap<QString, QString>);
|
||||
|
||||
private slots:
|
||||
// Will emit the signal
|
||||
void GetLibrariesFinished(QNetworkReply *reply);
|
||||
|
||||
void FetchAndCheckFolderItemsFinished(QNetworkReply *reply, const SeafileTree::Entry &library, const QString &path);
|
||||
|
||||
// Add recursively the content of a folder from a library
|
||||
void AddRecursivelyFolderItemsFinished(QNetworkReply *reply, const QString &library, const QString &path);
|
||||
// Get the url and try to add the file to the database
|
||||
void FetchContentUrlForFileFinished(QNetworkReply* reply, const Song &song, const QString &mime_type);
|
||||
// Add the entry to the tree and maybe add this entry to the database
|
||||
void AddEntry(const QString &library, const QString &path, const SeafileTree::Entry &entry);
|
||||
// Update the entry or check recursively the directories
|
||||
void UpdateEntry(const QString &library, const QString &path, const SeafileTree::Entry &entry);
|
||||
// Delete the entry (eventually the files of its subdir) of the tree and the database
|
||||
void DeleteEntry(const QString &library, const QString &path, const SeafileTree::Entry &entry);
|
||||
|
||||
void UpdateLibrariesInProgress(const QMap<QString, QString> &libraries);
|
||||
|
||||
void MaybeAddFileEntryInProgress(QNetworkReply *reply, const QString &library,
|
||||
const QString &path, const QString &mime_type);
|
||||
|
||||
private:
|
||||
QString access_token() const;
|
||||
bool is_authenticated() const;
|
||||
|
||||
void AddAuthorizationHeader(QNetworkRequest* request) const;
|
||||
|
||||
void UpdateLibraries();
|
||||
|
||||
void FetchAndCheckFolderItems(const SeafileTree::Entry &library, const QString &path);
|
||||
void AddRecursivelyFolderItems(const QString &library, const QString &path);
|
||||
|
||||
QNetworkReply* PrepareFetchFolderItems(const QString &library, const QString &path);
|
||||
QNetworkReply* PrepareFetchContentForFile(const QString &library, const QString &filepath);
|
||||
QNetworkReply* PrepareFetchContentUrlForFile(const QString &library, const QString &filepath);
|
||||
|
||||
void MaybeAddFileEntry(const QString &entry_name, const QString &library, const QString &path);
|
||||
|
||||
// False if not 200 or 429
|
||||
// If 429 (too many requests), re execute the request and put the reply in the argument
|
||||
bool CheckReply(QNetworkReply **reply);
|
||||
|
||||
|
||||
SeafileTree tree_;
|
||||
QString access_token_;
|
||||
QString server_;
|
||||
QString library_updated_;
|
||||
};
|
||||
|
||||
#endif // SEAFILESERVICE_H
|
150
src/internet/seafilesettingspage.cpp
Normal file
150
src/internet/seafilesettingspage.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "seafileservice.h"
|
||||
#include "seafilesettingspage.h"
|
||||
#include "internetmodel.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/network.h"
|
||||
#include "ui_seafilesettingspage.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSettings>
|
||||
#include <QtDebug>
|
||||
|
||||
SeafileSettingsPage::SeafileSettingsPage(SettingsDialog* dialog)
|
||||
: SettingsPage(dialog),
|
||||
ui_(new Ui_SeafileSettingsPage),
|
||||
service_(InternetModel::Service<SeafileService>()) {
|
||||
ui_->setupUi(this);
|
||||
|
||||
setWindowIcon(QIcon(":/providers/seafile.png"));
|
||||
|
||||
connect(ui_->login_button, SIGNAL(clicked()), SLOT(Login()));
|
||||
connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(Logout()));
|
||||
|
||||
ui_->login_state->AddCredentialField(ui_->server);
|
||||
ui_->login_state->AddCredentialField(ui_->mail);
|
||||
ui_->login_state->AddCredentialField(ui_->password);
|
||||
ui_->login_state->AddCredentialGroup(ui_->account_group);
|
||||
|
||||
ui_->library_box->addItem("None", "none");
|
||||
|
||||
connect(service_, SIGNAL(GetLibrariesFinishedSignal(QMap<QString,QString>)), this,
|
||||
SLOT(GetLibrariesFinished(QMap<QString,QString>)));
|
||||
}
|
||||
|
||||
SeafileSettingsPage::~SeafileSettingsPage() { delete ui_; }
|
||||
|
||||
|
||||
void SeafileSettingsPage::Load() {
|
||||
QSettings s;
|
||||
s.beginGroup(SeafileService::kSettingsGroup);
|
||||
|
||||
ui_->server->setText(s.value("server").toString());
|
||||
ui_->mail->setText(s.value("mail").toString());
|
||||
|
||||
if (!ui_->server->text().isEmpty() && !ui_->mail->text().isEmpty()) {
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, ui_->mail->text());
|
||||
|
||||
// If there is more than "none" library, that means that we already got the libraries
|
||||
if(ui_->library_box->count() <= 1) {
|
||||
service_->GetLibraries();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SeafileSettingsPage::GetLibrariesFinished(QMap<QString, QString> libraries) {
|
||||
ui_->library_box->clear();
|
||||
ui_->library_box->addItem("None", "none");
|
||||
ui_->library_box->addItem("All (could be slow)", "all");
|
||||
|
||||
// key : library's id, value : library's name
|
||||
QMapIterator <QString, QString> library(libraries);
|
||||
while(library.hasNext()) {
|
||||
library.next();
|
||||
ui_->library_box->addItem(library.value(), library.key());
|
||||
}
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(SeafileService::kSettingsGroup);
|
||||
QString library_id = s.value("library").toString();
|
||||
|
||||
int saved_index = ui_->library_box->findData(library_id);
|
||||
if (saved_index != -1) {
|
||||
ui_->library_box->setCurrentIndex(saved_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SeafileSettingsPage::Save() {
|
||||
QString id = ui_->library_box->itemData(ui_->library_box->currentIndex()).toString();
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(SeafileService::kSettingsGroup);
|
||||
|
||||
s.setValue("mail", ui_->mail->text());
|
||||
s.setValue("server", ui_->server->text());
|
||||
s.setValue("library", id);
|
||||
// Don't need to save the password
|
||||
|
||||
service_->ChangeLibrary(id);
|
||||
}
|
||||
|
||||
|
||||
void SeafileSettingsPage::Login() {
|
||||
|
||||
ui_->login_button->setEnabled(false);
|
||||
|
||||
if(service_->GetToken(ui_->mail->text(), ui_->password->text(), ui_->server->text())) {
|
||||
Save();
|
||||
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, ui_->mail->text());
|
||||
|
||||
service_->GetLibraries();
|
||||
}
|
||||
else {
|
||||
ui_->login_button->setEnabled(true);
|
||||
QMessageBox::warning(this, tr("Unable to connect"), tr("Unable to connect"));
|
||||
}
|
||||
}
|
||||
|
||||
void SeafileSettingsPage::Logout() {
|
||||
service_->ForgetCredentials();
|
||||
|
||||
// We choose to keep the server
|
||||
ui_->mail->clear();
|
||||
ui_->password->clear();
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(SeafileService::kSettingsGroup);
|
||||
|
||||
s.remove("mail");
|
||||
s.remove("server");
|
||||
s.remove("library");
|
||||
|
||||
ui_->library_box->clear();
|
||||
ui_->library_box->addItem("None", "none");
|
||||
|
||||
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedOut);
|
||||
ui_->login_button->setEnabled(true);
|
||||
|
||||
}
|
||||
|
51
src/internet/seafilesettingspage.h
Normal file
51
src/internet/seafilesettingspage.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEAFILESETTINGSPAGE_H
|
||||
#define SEAFILESETTINGSPAGE_H
|
||||
|
||||
#include "ui/settingspage.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QWidget>
|
||||
|
||||
class Ui_SeafileSettingsPage;
|
||||
class SeafileService;
|
||||
|
||||
class SeafileSettingsPage : public SettingsPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SeafileSettingsPage(SettingsDialog* dialog);
|
||||
~SeafileSettingsPage();
|
||||
|
||||
void Load();
|
||||
void Save();
|
||||
|
||||
private slots:
|
||||
void Login();
|
||||
void Logout();
|
||||
// Map -> key : library's id, value : library's name
|
||||
void GetLibrariesFinished(QMap<QString, QString> libraries);
|
||||
|
||||
private:
|
||||
Ui_SeafileSettingsPage* ui_;
|
||||
SeafileService* service_;
|
||||
|
||||
};
|
||||
|
||||
#endif // SEAFILESETTINGSPAGE_H
|
175
src/internet/seafilesettingspage.ui
Normal file
175
src/internet/seafilesettingspage.ui
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SeafileSettingsPage</class>
|
||||
<widget class="QWidget" name="SeafileSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>480</width>
|
||||
<height>261</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Seafile</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="LoginStateWidget" name="login_state" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="account_group">
|
||||
<property name="title">
|
||||
<string>Account details</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QWidget" name="login_container" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="password_label">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="mail_label">
|
||||
<property name="text">
|
||||
<string>Email</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="login_button">
|
||||
<property name="text">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="server_label">
|
||||
<property name="text">
|
||||
<string>Server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="mail">
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="server">
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="preference_group">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Preference</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="gridLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>19</y>
|
||||
<width>431</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="preference_container">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="library_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="library_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LoginStateWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/loginstatewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>server</tabstop>
|
||||
<tabstop>mail</tabstop>
|
||||
<tabstop>password</tabstop>
|
||||
<tabstop>login_button</tabstop>
|
||||
<tabstop>library_box</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
406
src/internet/seafiletree.cpp
Normal file
406
src/internet/seafiletree.cpp
Normal file
@ -0,0 +1,406 @@
|
||||
#include "seafiletree.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QRegExp>
|
||||
#include <QDir>
|
||||
#include "core/logging.h"
|
||||
|
||||
/* ############################## SeafileTree ############################## */
|
||||
|
||||
SeafileTree::SeafileTree() { }
|
||||
|
||||
SeafileTree::SeafileTree(const SeafileTree ©) : SeafileTree()
|
||||
{ libraries_ = copy.libraries(); }
|
||||
|
||||
QList<SeafileTree::TreeItem*> SeafileTree::libraries() const { return libraries_; }
|
||||
|
||||
void SeafileTree::Print() const {
|
||||
qLog(Debug) << "library count : " << libraries_.count();
|
||||
|
||||
for (TreeItem *item : libraries_) {
|
||||
qLog(Debug) << "library : " << item->ToString(1);
|
||||
}
|
||||
}
|
||||
|
||||
void SeafileTree::AddLibrary(const QString &name, const QString &id) {
|
||||
libraries_.append(
|
||||
new TreeItem(
|
||||
Entry(name, id, Entry::Type::LIBRARY)));
|
||||
}
|
||||
|
||||
void SeafileTree::DeleteLibrary(const QString &id) {
|
||||
for (int i = 0; i < libraries_.size(); ++i) {
|
||||
if (libraries_.at(i)->entry().id() == id) {
|
||||
libraries_.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SeafileTree::AddEntry(const QString& library, const QString &path, const Entry &entry) {
|
||||
TreeItem *dir_node = FindFromAbsolutePath(library, path);
|
||||
|
||||
if(!dir_node) {
|
||||
qLog(Warning) << "Can't find the path...";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it is not a dir or a library we can't add an entry...
|
||||
if(!dir_node->entry().is_dir() && !dir_node->entry().is_library()) {
|
||||
qLog(Warning) << "This is not a dir or a file...";
|
||||
return false;
|
||||
}
|
||||
|
||||
dir_node->AppendChild(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SeafileTree::CheckEntries(const Entries &server_entries, const Entry &library, const QString &path) {
|
||||
TreeItem *local_item = FindFromAbsolutePath(library.id(), path);
|
||||
|
||||
// Don't know the path
|
||||
// Have to add all entries
|
||||
if(!local_item) {
|
||||
emit ToAdd(library.id(), path, library);
|
||||
return;
|
||||
}
|
||||
|
||||
Entries local_entries = local_item->childs_entry();
|
||||
|
||||
for (const Entry &server_entry : server_entries) {
|
||||
bool is_in_tree = false;
|
||||
|
||||
for (int i=0; i<local_entries.size(); ++i) {
|
||||
Entry local_entry = local_entries.at(i);
|
||||
|
||||
// We found the entry in the tree
|
||||
if (local_entry.name() == server_entry.name() && local_entry.type() == server_entry.type()) {
|
||||
is_in_tree = true;
|
||||
|
||||
// Need to update
|
||||
if (local_entry.id() != server_entry.id()) {
|
||||
emit ToUpdate(library.id(), path, server_entry);
|
||||
|
||||
// Set the new id to the local entry
|
||||
local_entry.set_id(server_entry.id());
|
||||
}
|
||||
|
||||
// local_entries could be named "local_entries_to_proceed"
|
||||
// So we delete from the list the entry we just proceeded
|
||||
local_entries.removeAt(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to add the entry
|
||||
if (!is_in_tree){
|
||||
emit ToAdd(library.id(), path, server_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Each entry in this list corresponds to an entry that we didn't proceed
|
||||
// So if the entry is in the tree but not on the server : we have to delete it
|
||||
for (const Entry &local_entry : local_entries) {
|
||||
emit ToDelete(library.id(), path, local_entry);
|
||||
}
|
||||
}
|
||||
|
||||
SeafileTree::TreeItem* SeafileTree::FindLibrary(const QString &library) {
|
||||
for (TreeItem *item : libraries_) {
|
||||
if (item->entry().id() == library)
|
||||
return item;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SeafileTree::TreeItem* SeafileTree::FindFromAbsolutePath(const QString &library, const QString &path) {
|
||||
TreeItem *node_item = FindLibrary(library);
|
||||
|
||||
if (!node_item) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QStringList path_parts = path.split("/", QString::SkipEmptyParts);
|
||||
|
||||
for (const QString &part : path_parts) {
|
||||
node_item = node_item->FindChild(part);
|
||||
|
||||
if(!node_item) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return node_item;
|
||||
}
|
||||
|
||||
bool SeafileTree::DeleteEntry(const QString &library, const QString &path, const Entry &entry) {
|
||||
TreeItem *item_parent = FindFromAbsolutePath(library, path);
|
||||
|
||||
if(!item_parent) {
|
||||
qLog(Debug) << "Unable to delete " << library + path + entry.name()
|
||||
<< " : path " << path << " not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
TreeItem *item_entry = item_parent->FindChild(entry.name());
|
||||
|
||||
if(!item_entry) {
|
||||
qLog(Debug) << "Unable to delete " << library + path + entry.name()
|
||||
<< " : entry " << entry.name() << " from path not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!item_parent->RemoveChild(item_entry)) {
|
||||
qLog(Debug) << "Can't remove " << item_entry->entry().name() << " from parent";
|
||||
return false;
|
||||
}
|
||||
|
||||
delete item_entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SeafileTree::Clear() {
|
||||
qDeleteAll(libraries_);
|
||||
libraries_.clear();
|
||||
}
|
||||
|
||||
|
||||
QList<QPair<QString, SeafileTree::Entry>> SeafileTree::GetRecursiveFilesOfDir(const QString &path, const TreeItem *item) {
|
||||
// key = path, value = entry
|
||||
QList<QPair<QString, Entry>> files;
|
||||
|
||||
if(!item) {
|
||||
return files;
|
||||
}
|
||||
|
||||
if(item->entry().is_file()) {
|
||||
files.append(qMakePair(path, item->entry()));
|
||||
}
|
||||
// Get files of the dir
|
||||
else {
|
||||
for (TreeItem *child_item : item->childs()) {
|
||||
if(child_item->entry().is_file()) {
|
||||
files.append(qMakePair(path, child_item->entry()));
|
||||
}
|
||||
else {
|
||||
QString name = child_item->entry().name() + "/";
|
||||
files.append(GetRecursiveFilesOfDir(path + name, child_item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
SeafileTree::~SeafileTree() {}
|
||||
|
||||
|
||||
|
||||
/* ################################# Entry ################################# */
|
||||
|
||||
SeafileTree::Entry::Entry()
|
||||
: Entry("", "", Type::FILE) {}
|
||||
|
||||
SeafileTree::Entry::Entry(const SeafileTree::Entry &entry)
|
||||
: Entry(entry.name(), entry.id(), entry.type()) {}
|
||||
|
||||
SeafileTree::Entry::Entry(const QString& name, const QString& id, const Type& type) {
|
||||
name_ = name;
|
||||
id_ = id;
|
||||
type_ = type;
|
||||
}
|
||||
|
||||
QString SeafileTree::Entry::name() const { return name_; }
|
||||
QString SeafileTree::Entry::id() const { return id_; }
|
||||
SeafileTree::Entry::Type SeafileTree::Entry::type() const { return type_; }
|
||||
bool SeafileTree::Entry::is_dir() const { return (type_ == Entry::DIR); }
|
||||
bool SeafileTree::Entry::is_file() const { return (type_ == Entry::FILE); }
|
||||
bool SeafileTree::Entry::is_library() const { return (type_ == Entry::LIBRARY); }
|
||||
void SeafileTree::Entry::set_name(const QString &name) { name_ = name; }
|
||||
void SeafileTree::Entry::set_id(const QString &id) { id_ = id; }
|
||||
void SeafileTree::Entry::set_type(const Type &type) { type_ = type; }
|
||||
|
||||
|
||||
QString SeafileTree::Entry::ToString() const {
|
||||
return "name : " + name_ + " id : " + id_ + " type : " + TypeToString(type_);
|
||||
}
|
||||
|
||||
SeafileTree::Entry& SeafileTree::Entry::operator =(const Entry &entry) {
|
||||
name_ = entry.name();
|
||||
id_ = entry.id();
|
||||
type_ = entry.type();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SeafileTree::Entry::operator ==(const Entry &a) const {
|
||||
if(a.name() == name() && a.id() == id() && a.type() == type())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SeafileTree::Entry::operator !=(const Entry &a) const {
|
||||
return !(operator ==(a));
|
||||
}
|
||||
|
||||
SeafileTree::Entry::~Entry() {}
|
||||
|
||||
|
||||
QString SeafileTree::Entry::TypeToString(const Type &type) {
|
||||
if (type == DIR) {
|
||||
return "dir";
|
||||
}
|
||||
else if (type == FILE) {
|
||||
return "file";
|
||||
}
|
||||
else if (type == LIBRARY) {
|
||||
return "library";
|
||||
}
|
||||
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
SeafileTree::Entry::Type SeafileTree::Entry::StringToType(const QString &type) {
|
||||
if (type == "dir") {
|
||||
return DIR;
|
||||
}
|
||||
else if (type == "file") {
|
||||
return FILE;
|
||||
}
|
||||
else if (type == "library") {
|
||||
return LIBRARY;
|
||||
}
|
||||
|
||||
return NONE;
|
||||
}
|
||||
|
||||
|
||||
/* ############################### TreeItem ############################### */
|
||||
|
||||
SeafileTree::TreeItem::TreeItem() { entry_ = Entry(); }
|
||||
|
||||
SeafileTree::TreeItem::TreeItem(const TreeItem ©) { entry_ = copy.entry(); }
|
||||
|
||||
SeafileTree::TreeItem::TreeItem(const Entry &entry) { entry_ = entry; }
|
||||
|
||||
SeafileTree::TreeItem::TreeItem(const Entry &entry, QList<TreeItem *> *childs) {
|
||||
entry_ = entry;
|
||||
childs_ = *childs;
|
||||
}
|
||||
|
||||
SeafileTree::Entries SeafileTree::TreeItem::childs_entry() const {
|
||||
Entries entries;
|
||||
|
||||
for (TreeItem *item : childs_) {
|
||||
entries.append(Entry(item->entry()));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
SeafileTree::TreeItem* SeafileTree::TreeItem::child(int i) const { return childs_.at(i); }
|
||||
QList<SeafileTree::TreeItem*> SeafileTree::TreeItem::childs() const { return childs_; }
|
||||
SeafileTree::Entry SeafileTree::TreeItem::entry() const { return entry_; }
|
||||
void SeafileTree::TreeItem::set_entry(const Entry &entry) { entry_ = entry;}
|
||||
void SeafileTree::TreeItem::set_childs(QList<TreeItem *> *childs) { childs_ = *childs; }
|
||||
|
||||
void SeafileTree::TreeItem::AppendChild(TreeItem *child) { childs_.append(child); }
|
||||
|
||||
void SeafileTree::TreeItem::AppendChild(const Entry &entry) {
|
||||
childs_.append(new TreeItem(entry));
|
||||
}
|
||||
|
||||
bool SeafileTree::TreeItem::RemoveChild(TreeItem *child) { return childs_.removeOne(child); }
|
||||
|
||||
SeafileTree::TreeItem* SeafileTree::TreeItem::FindChild(const QString &name) const {
|
||||
for (TreeItem *item : childs_) {
|
||||
if (item->entry().name() == name)
|
||||
return item;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString SeafileTree::TreeItem::ToString(int i) const {
|
||||
QString res = "";
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
res += " ";
|
||||
}
|
||||
|
||||
res += entry_.ToString() + "\n";
|
||||
|
||||
for (TreeItem *item : childs_) {
|
||||
res += item->ToString( i+1 );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SeafileTree::TreeItem::~TreeItem() {
|
||||
// We need to delete childs
|
||||
for (TreeItem *item : childs_) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
QDataStream & operator << (QDataStream &out, const SeafileTree::Entry &entry) {
|
||||
out << entry.name_
|
||||
<< entry.id_
|
||||
<< static_cast<quint8>(entry.type_);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream & operator >> (QDataStream &in, SeafileTree::Entry &entry) {
|
||||
quint8 temp;
|
||||
|
||||
in >> entry.name_;
|
||||
in >> entry.id_;
|
||||
in >> temp;
|
||||
entry.type_ = SeafileTree::Entry::Type(temp);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
QDataStream & operator << (QDataStream &out, SeafileTree::TreeItem *item) {
|
||||
out << item->entry_
|
||||
<< item->childs_;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
QDataStream & operator >> (QDataStream &in, SeafileTree::TreeItem *&item) {
|
||||
SeafileTree::Entry *entry = new SeafileTree::Entry();
|
||||
QList<SeafileTree::TreeItem*> *childs = new QList<SeafileTree::TreeItem*>;
|
||||
|
||||
in >> *entry;
|
||||
in >> *childs;
|
||||
|
||||
item = new SeafileTree::TreeItem(*entry, childs);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
QDataStream & operator << (QDataStream &out, const SeafileTree &tree) {
|
||||
out << tree.libraries_;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream & operator >> (QDataStream &in, SeafileTree &tree) {
|
||||
in >> tree.libraries_;
|
||||
|
||||
return in;
|
||||
}
|
162
src/internet/seafiletree.h
Normal file
162
src/internet/seafiletree.h
Normal file
@ -0,0 +1,162 @@
|
||||
/* Contacts (for explanations, congratulations, insults) :
|
||||
* - <florian.bigard@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef SEAFILETREE_H
|
||||
#define SEAFILETREE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
|
||||
#include "cloudfileservice.h"
|
||||
|
||||
// Reproduce the file system of Seafile server libraries
|
||||
// Analog to a tree
|
||||
class SeafileTree : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
SeafileTree();
|
||||
SeafileTree(const SeafileTree ©);
|
||||
~SeafileTree();
|
||||
|
||||
class Entry {
|
||||
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
DIR = 0,
|
||||
FILE = 1,
|
||||
LIBRARY = 2,
|
||||
NONE = 3
|
||||
};
|
||||
|
||||
Entry();
|
||||
Entry(const Entry &entry);
|
||||
Entry(const QString &name, const QString &id, const Type &type);
|
||||
~Entry();
|
||||
|
||||
QString name() const;
|
||||
void set_name(const QString &name);
|
||||
QString id() const;
|
||||
void set_id(const QString &id);
|
||||
Type type() const;
|
||||
void set_type(const Type &type);
|
||||
|
||||
bool is_dir() const;
|
||||
bool is_file() const;
|
||||
bool is_library() const;
|
||||
|
||||
Entry& operator =(const Entry &entry);
|
||||
bool operator ==(const Entry &a) const;
|
||||
bool operator !=(const Entry &a) const;
|
||||
|
||||
QString ToString() const;
|
||||
|
||||
static QString TypeToString(const Type &type);
|
||||
static Type StringToType(const QString &type);
|
||||
|
||||
private:
|
||||
QString name_, id_;
|
||||
Type type_;
|
||||
|
||||
friend QDataStream & operator << (QDataStream &out, const SeafileTree::Entry &entry);
|
||||
friend QDataStream & operator >> (QDataStream &in, SeafileTree::Entry &entry);
|
||||
};
|
||||
|
||||
typedef QList<Entry> Entries;
|
||||
|
||||
// Node of the tree
|
||||
// Contains an entry
|
||||
class TreeItem {
|
||||
public:
|
||||
TreeItem();
|
||||
TreeItem(const TreeItem ©);
|
||||
TreeItem(const Entry &entry);
|
||||
TreeItem(const Entry &entry, QList<TreeItem *> *childs);
|
||||
~TreeItem();
|
||||
|
||||
TreeItem* child(int i) const;
|
||||
QList<TreeItem*> childs() const;
|
||||
// List of each child's entry
|
||||
Entries childs_entry() const;
|
||||
|
||||
void set_childs(QList<TreeItem*> *childs);
|
||||
|
||||
Entry entry() const;
|
||||
void set_entry(const Entry &entry);
|
||||
|
||||
void AppendChild(TreeItem *child);
|
||||
void AppendChild(const Entry &entry);
|
||||
|
||||
// True if child is removed
|
||||
bool RemoveChild(TreeItem *child);
|
||||
|
||||
// nullptr if we didn't find a child entry with the given name
|
||||
TreeItem* FindChild(const QString &name) const;
|
||||
|
||||
// Convert the node in QString (for debug)
|
||||
QString ToString(int i) const;
|
||||
|
||||
private:
|
||||
Entry entry_;
|
||||
QList<TreeItem*> childs_;
|
||||
|
||||
friend QDataStream & operator << (QDataStream &out, SeafileTree::TreeItem *item);
|
||||
friend QDataStream & operator >> (QDataStream &in, SeafileTree::TreeItem *&item);
|
||||
};
|
||||
|
||||
QList<TreeItem*> libraries() const;
|
||||
|
||||
void AddLibrary(const QString &name, const QString &id);
|
||||
void DeleteLibrary(const QString &id);
|
||||
bool AddEntry(const QString &library, const QString &path, const Entry &entry);
|
||||
bool DeleteEntry(const QString &library, const QString &path, const Entry &entry);
|
||||
|
||||
// Get a list of pair (path, entry) corresponding to the subfiles (and recursively to the subsubfiles...) of the given item
|
||||
QList<QPair<QString, SeafileTree::Entry>> GetRecursiveFilesOfDir(const QString &path, const TreeItem *item);
|
||||
|
||||
// nullptr if we didn't find the library with the given id
|
||||
TreeItem* FindLibrary(const QString &library);
|
||||
// nullptr if we didn't find the item
|
||||
TreeItem* FindFromAbsolutePath(const QString &library, const QString &path);
|
||||
|
||||
// Compare the server entries with the tree
|
||||
// Emit signals (ToDelete, ToAdd, ToUpdate)
|
||||
void CheckEntries(const Entries &server_entries, const Entry &library, const QString &path);
|
||||
|
||||
// Destroy the tree
|
||||
void Clear();
|
||||
|
||||
// Print the tree in the debug log
|
||||
void Print() const;
|
||||
|
||||
signals:
|
||||
// Entry to delete in the tree
|
||||
void ToDelete(const QString &library, const QString &path, const SeafileTree::Entry &entry);
|
||||
// Entry to add in the tree
|
||||
void ToAdd(const QString &library, const QString &path, const SeafileTree::Entry &entry);
|
||||
// Entry to update in the tree
|
||||
void ToUpdate(const QString &library, const QString &path, const SeafileTree::Entry &entry);
|
||||
|
||||
private:
|
||||
QList<TreeItem*> libraries_;
|
||||
|
||||
friend QDataStream & operator << (QDataStream &out, const SeafileTree &tree);
|
||||
friend QDataStream & operator >> (QDataStream &in, SeafileTree &tree);
|
||||
};
|
||||
|
||||
QDataStream & operator << (QDataStream &out, const SeafileTree &tree);
|
||||
QDataStream & operator >> (QDataStream &in, SeafileTree &tree);
|
||||
|
||||
QDataStream & operator << (QDataStream &out, const SeafileTree::Entry &entry);
|
||||
QDataStream & operator >> (QDataStream &in, SeafileTree::Entry &entry);
|
||||
|
||||
QDataStream & operator << (QDataStream &out, SeafileTree::TreeItem *item);
|
||||
QDataStream & operator >> (QDataStream &in, SeafileTree::TreeItem *&item);
|
||||
|
||||
|
||||
#endif // SEAFILETREE_H
|
22
src/internet/seafileurlhandler.cpp
Normal file
22
src/internet/seafileurlhandler.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "seafileurlhandler.h"
|
||||
|
||||
#include "seafileservice.h"
|
||||
|
||||
SeafileUrlHandler::SeafileUrlHandler(SeafileService* service, QObject* parent)
|
||||
: UrlHandler(parent), service_(service) {}
|
||||
|
||||
UrlHandler::LoadResult SeafileUrlHandler::StartLoading(const QUrl& url) {
|
||||
QString file_library_and_path = url.path();
|
||||
QRegExp reg("/([^/]+)(/.*)$");
|
||||
|
||||
if(reg.indexIn(file_library_and_path) == -1) {
|
||||
qLog(Debug) << "Can't find repo and file path in " << url;
|
||||
}
|
||||
|
||||
QString library = reg.cap(1);
|
||||
QString filepath = reg.cap(2);
|
||||
|
||||
QUrl real_url = service_->GetStreamingUrlFromSongId(library, filepath);
|
||||
|
||||
return LoadResult(url, LoadResult::TrackAvailable, real_url);
|
||||
}
|
21
src/internet/seafileurlhandler.h
Normal file
21
src/internet/seafileurlhandler.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef SEAFILEURLHANDLER_H
|
||||
#define SEAFILEURLHANDLER_H
|
||||
|
||||
#include "core/urlhandler.h"
|
||||
|
||||
class SeafileService;
|
||||
|
||||
class SeafileUrlHandler : public UrlHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SeafileUrlHandler(SeafileService* service, QObject* parent = nullptr);
|
||||
|
||||
QString scheme() const { return "seafile"; }
|
||||
QIcon icon() const { return QIcon(":/providers/seafile.png"); }
|
||||
LoadResult StartLoading(const QUrl& url);
|
||||
|
||||
private:
|
||||
SeafileService* service_;
|
||||
};
|
||||
|
||||
#endif // SEAFILEURLHANDLER_H
|
@ -79,6 +79,12 @@
|
||||
#include "internet/skydrivesettingspage.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SEAFILE
|
||||
#include "internet/seafilesettingspage.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPainter>
|
||||
@ -183,6 +189,12 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
|
||||
AddPage(Page_Vk, new VkSettingsPage(this), providers);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SEAFILE
|
||||
AddPage(Page_Seafile, new SeafileSettingsPage(this), providers);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
AddPage(Page_Magnatune, new MagnatuneSettingsPage(this), providers);
|
||||
AddPage(Page_DigitallyImported, new DigitallyImportedSettingsPage(this),
|
||||
providers);
|
||||
|
@ -84,7 +84,8 @@ class SettingsDialog : public QDialog {
|
||||
Page_Dropbox,
|
||||
Page_Skydrive,
|
||||
Page_Box,
|
||||
Page_Vk
|
||||
Page_Vk,
|
||||
Page_Seafile
|
||||
};
|
||||
|
||||
enum Role { Role_IsSeparator = Qt::UserRole };
|
||||
|
Loading…
Reference in New Issue
Block a user