size = 0;
}
+PendingMyListUpdate::PendingMyListUpdate()
+{
+ fid = 0;
+ myState = 0;
+ myFileState = 0;
+ setMyWatched = false;
+ setMyState = false;
+ setMyFileState = false;
+ setMyStorage = false;
+ setMySource = false;
+ setMyOther = false;
+}
+
HostInfo::HostInfo()
{
id = 0;
QSqlQuery getRequestBatchQuery;
QSqlQuery clearRequestQuery;
+ QSqlQuery addPendingMyListUpdateQuery;
+ QSqlQuery getPendingMyListUpdateQuery;
+ QSqlQuery getPendingMyListUpdateBatchQuery;
+ QSqlQuery clearPendingMyListUpdateQuery;
+
QThread *thread;
};
return ret;
}
+bool Database::addPendingMyListUpdate(const PendingMyListUpdate &request)
+{
+ d->addPendingMyListUpdateQuery.bindValue(":fid", request.fid);
+ d->addPendingMyListUpdateQuery.bindValue(":setMyWatched", request.setMyWatched);
+ d->addPendingMyListUpdateQuery.bindValue(":myWatched", request.myWatched);
+ d->addPendingMyListUpdateQuery.bindValue(":setMyState", request.setMyState);
+ d->addPendingMyListUpdateQuery.bindValue(":myState", request.myState);
+ d->addPendingMyListUpdateQuery.bindValue(":setMyFileState", request.setMyFileState);
+ d->addPendingMyListUpdateQuery.bindValue(":myFileState", request.myFileState);
+ d->addPendingMyListUpdateQuery.bindValue(":setMyStorage", request.setMyStorage);
+ d->addPendingMyListUpdateQuery.bindValue(":myStorage", request.myStorage);
+ d->addPendingMyListUpdateQuery.bindValue(":setMySource", request.setMySource);
+ d->addPendingMyListUpdateQuery.bindValue(":mySource", request.mySource);
+ d->addPendingMyListUpdateQuery.bindValue(":setMyOther", request.setMyOther);
+ d->addPendingMyListUpdateQuery.bindValue(":myOther", request.myOther);
+
+ return exec(d->addPendingMyListUpdateQuery);
+}
+
+PendingMyListUpdate Database::getPendingMyListUpdate(qint64 updateId)
+{
+ d->getPendingMyListUpdateQuery.bindValue(":updateId", updateId);
+
+ PendingMyListUpdate request;
+
+ if (!exec(d->getPendingMyListUpdateQuery))
+ return request;
+
+ if (d->getPendingMyListUpdateQuery.next())
+ {
+ request.updateId = d->getPendingMyListUpdateQuery.value(0).toLongLong();
+ request.fid = d->getPendingMyListUpdateQuery.value(1).toInt();
+ request.setMyWatched = d->getPendingMyListUpdateQuery.value(2).toBool();
+ request.myWatched = d->getPendingMyListUpdateQuery.value(3).toDateTime();
+ request.setMyState = d->getPendingMyListUpdateQuery.value(4).toBool();
+ request.myState = d->getPendingMyListUpdateQuery.value(5).toInt();
+ request.setMyFileState = d->getPendingMyListUpdateQuery.value(6).toBool();
+ request.myFileState = d->getPendingMyListUpdateQuery.value(7).toInt();
+ request.setMyStorage = d->getPendingMyListUpdateQuery.value(8).toBool();
+ request.myStorage = d->getPendingMyListUpdateQuery.value(9).toString();
+ request.setMySource = d->getPendingMyListUpdateQuery.value(10).toBool();
+ request.mySource = d->getPendingMyListUpdateQuery.value(11).toString();
+ request.setMyOther = d->getPendingMyListUpdateQuery.value(12).toBool();
+ request.myOther = d->getPendingMyListUpdateQuery.value(13).toString();
+ request.added = d->getPendingMyListUpdateQuery.value(14).toDateTime();
+ request.started = d->getPendingMyListUpdateQuery.value(15).toDateTime();
+ }
+ return request;
+}
+
+QList<PendingMyListUpdate> Database::getPendingMyListUpdateBatch(int limit)
+{
+ d->getPendingMyListUpdateBatchQuery.bindValue(":limit", limit);
+
+ QList<PendingMyListUpdate> ret;
+
+ if (!exec(d->getPendingMyListUpdateBatchQuery))
+ return ret;
+
+ while (d->getPendingMyListUpdateBatchQuery.next())
+ {
+ PendingMyListUpdate request;
+ request.updateId = d->getPendingMyListUpdateBatchQuery.value(0).toLongLong();
+ request.fid = d->getPendingMyListUpdateBatchQuery.value(1).toInt();
+ request.setMyWatched = d->getPendingMyListUpdateBatchQuery.value(2).toBool();
+ request.myWatched = d->getPendingMyListUpdateBatchQuery.value(3).toDateTime();
+ request.setMyState = d->getPendingMyListUpdateBatchQuery.value(4).toBool();
+ request.myState = d->getPendingMyListUpdateBatchQuery.value(5).toInt();
+ request.setMyFileState = d->getPendingMyListUpdateBatchQuery.value(6).toBool();
+ request.myFileState = d->getPendingMyListUpdateBatchQuery.value(7).toInt();
+ request.setMyStorage = d->getPendingMyListUpdateBatchQuery.value(8).toBool();
+ request.myStorage = d->getPendingMyListUpdateBatchQuery.value(9).toString();
+ request.setMySource = d->getPendingMyListUpdateBatchQuery.value(10).toBool();
+ request.mySource = d->getPendingMyListUpdateBatchQuery.value(11).toString();
+ request.setMyOther = d->getPendingMyListUpdateBatchQuery.value(12).toBool();
+ request.myOther = d->getPendingMyListUpdateBatchQuery.value(13).toString();
+ request.added = d->getPendingMyListUpdateBatchQuery.value(14).toDateTime();
+ request.started = d->getPendingMyListUpdateBatchQuery.value(15).toDateTime();
+ ret << request;
+ }
+
+ d->getPendingMyListUpdateBatchQuery.finish();
+
+ return ret;
+}
+
+bool Database::clearPendingMyListUpdate(const PendingMyListUpdate &request)
+{
+ d->clearPendingMyListUpdateQuery.bindValue(":update_id", request.updateId);
+ return exec(d->clearPendingMyListUpdateQuery);
+}
+
bool Database::truncateTitleData()
{
return exec("TRUNCATE TABLE anime_title");
d->clearRequestQuery = QSqlQuery(d->db);
d->clearRequestQuery.prepare("DELETE FROM pending_request WHERE aid = :aid AND eid = :eid AND fid = :fid AND ed2k = :ed2k AND size = :size");
+ d->addPendingMyListUpdateQuery = QSqlQuery(d->db);
+ d->addPendingMyListUpdateQuery.prepare("INSERT INTO pending_mylist_update VALUES(DEFAULT, :fid, :setMyWatched, :myWatched, "
+ ":setMyState, :myState, :setMyFileState, :myFileState, :setMyStorage, :myStorage, "
+ ":setMySource, :mySource, :setMyOther, :myOther, DEFAULT, DEFAULT, DEFAULT)");
+
+ d->getPendingMyListUpdateBatchQuery = QSqlQuery(d->db);
+ d->getPendingMyListUpdateBatchQuery.prepare("UPDATE pending_mylist_update SET started = NOW() "
+ "WHERE update_id IN (SELECT update_id FROM pending_mylist_update "
+ " WHERE started IS NULL "
+ " ORDER BY added ASC "
+ " LIMIT :limit) "
+ "RETURNING update_id, fid, set_my_watched, my_watched, set_my_state, "
+ "my_state, set_my_file_state, my_file_state, set_my_storage, my_storage, "
+ "set_my_source, my_source, set_my_other, my_other, added, started");
+
+ d->getPendingMyListUpdateQuery = QSqlQuery(d->db);
+ d->getPendingMyListUpdateQuery.prepare("SELECT update_id, fid, set_my_watched, my_watched, set_my_state, my_state, "
+ "set_my_file_state, my_file_state, set_my_storage, my_storage, set_my_source, "
+ "my_source, set_my_other, my_other, added, started, finished "
+ "FROM pending_mylist_update WHERE update_id = :updateId");
+
+ d->clearPendingMyListUpdateQuery = QSqlQuery(d->db);
+ d->clearPendingMyListUpdateQuery.prepare("UPDATE pending_mylist_update SET finished = NOW() WHERE update_id = :updateId");
+
d->db.driver()->subscribeToNotification("new_pending_request");
+ d->db.driver()->subscribeToNotification("new_pending_mylist_update");
}
bool Database::exec(QSqlQuery &query)
{
emit newPendingRequest();
}
+ else if (notification == "new_pending_mylist_update")
+ {
+ emit newPendingMyListUpdate();
+ }
}
} // namespace LocalMyList
PendingRequest();
};
+struct LOCALMYLISTSHARED_EXPORT PendingMyListUpdate
+{
+ qint64 updateId;
+ int fid;
+ bool setMyWatched;
+ QDateTime myWatched;
+ bool setMyState;
+ int myState;
+ bool setMyFileState;
+ int myFileState;
+ bool setMyStorage;
+ QString myStorage;
+ bool setMySource;
+ QString mySource;
+ bool setMyOther;
+ QString myOther;
+
+ QDateTime added;
+ QDateTime started;
+ QDateTime finished;
+
+ PendingMyListUpdate();
+};
+
struct LOCALMYLISTSHARED_EXPORT HostInfo
{
int id;
QList<PendingRequest> getRequestBatch(int limit = 10);
bool clearRequest(const PendingRequest &request);
+ bool addPendingMyListUpdate(const PendingMyListUpdate &request);
+ PendingMyListUpdate getPendingMyListUpdate(qint64 updateId);
+ QList<PendingMyListUpdate> getPendingMyListUpdateBatch(int limit = 10);
+ bool clearPendingMyListUpdate(const PendingMyListUpdate &request);
+
bool truncateTitleData();
bool truncateMyListData();
bool truncateDatabase();
void disconnected();
void newPendingRequest();
+ void newPendingMyListUpdate();
private slots:
void handleNotification(const QString ¬ification);
return m_settings;
}
-
// -------
+void MyList::markWatched(int fid, QDateTime when)
+{
+ if (!fid)
+ return;
+
+ PendingMyListUpdate request;
+ request.fid = fid;
+ request.setMyWatched = true;
+ request.myWatched = when;
+ db->addPendingMyListUpdate(request);
+}
+
+
void MyList::setupUdpClient()
{
using namespace ::AniDBUdpClient;
m_requestHandler = new RequestHandler(db, this);
connect(db, SIGNAL(newPendingRequest()), m_requestHandler, SLOT(handleRequests()));
+ connect(db, SIGNAL(newPendingMyListUpdate()), m_requestHandler, SLOT(handleMyListUpdates()));
}
void MyList::loadLocalSettings()
#include <QSet>
#include <QDir>
#include <QFileInfo>
+#include <QDateTime>
namespace LocalMyList {
public slots:
void setHostName(QString name);
+ void markWatched(int fid, QDateTime when = QDateTime::currentDateTime());
AbstractTask *addFile(const QFileInfo &file);
AbstractTask *addDirectory(const QDir &directory);
{
this->db = db;
connect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()), Qt::QueuedConnection);
+ connect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
}
void RequestHandler::handleRequests()
emit batchFinished();
}
+void RequestHandler::handleMyListUpdates()
+{
+ using namespace ::AniDBUdpClient;
+
+ qDebug() << "handleMyListUpdates";
+
+ db->transaction();
+ QList<PendingMyListUpdate> requests = db->getPendingMyListUpdateBatch();
+
+ qDebug() << "Got" << requests.count() << "requests";
+
+ foreach (const PendingMyListUpdate &request, requests)
+ {
+ MyListAddCommand cmd(request.fid, true);
+
+ if (request.setMyWatched)
+ {
+ if (request.myWatched.isNull())
+ {
+ cmd.setViewed(false);
+ }
+ else
+ {
+ cmd.setViewed(true);
+ cmd.setViewDate(request.myWatched);
+ }
+ }
+ if (request.setMyState)
+ cmd.setState(State(request.myState));
+ if (request.setMyStorage)
+ cmd.setStorage(request.myStorage);
+ if (request.setMySource)
+ cmd.setSource(request.mySource);
+ if (request.setMyOther)
+ cmd.setOther(request.myOther);
+
+ MyListAddReply *reply = Client::instance()->send(cmd);
+ connect(reply, SIGNAL(replyReady(bool)), this, SLOT(myListEditReplyRecieved(bool)));
+ myListUpdateIdMap.insert(reply, request.updateId);
+ }
+}
+
void RequestHandler::animeRequestComplete(bool success)
{
using namespace ::AniDBUdpClient;
ed2kRequest.ed2k = reply->command().ed2k();
ed2kRequest.size = reply->command().size();
db->clearRequest(ed2kRequest);
+}
+void RequestHandler::myListEditReplyRecieved(bool success)
+{
+ using namespace ::AniDBUdpClient;
+
+ MyListAddReply *reply = qobject_cast<MyListAddReply *>(sender());
+ Q_ASSERT(reply);
+ Q_ASSERT(myListUpdateIdMap.contains(reply));
+ reply->deleteLater();
+
+ qint64 id = myListUpdateIdMap.take(reply);
+
+ if (!success)
+ return;
+
+ db->transaction();
+ File file = db->getFile(reply->command().fid());
+
+ if (!file.fid)
+ {
+ qWarning("Updated file not in DB");
+ return;
+ }
+
+ PendingMyListUpdate request = db->getPendingMyListUpdate(id);
+
+ if (!request.updateId)
+ {
+ qWarning("PendingMyListUpdate not in DB");
+ return;
+ }
+
+ if (request.setMyWatched)
+ file.myWatched = request.myWatched;
+ if (request.setMyState)
+ file.myState = request.myState;
+ if (request.setMyFileState)
+ file.myFileState = request.myFileState;
+ if (request.setMySource)
+ file.mySource = request.mySource.isNull() ? QString("") : request.mySource;
+ if (request.setMyStorage)
+ file.myStorage = request.myStorage.isNull() ? QString("") : request.myStorage;
+ if (request.setMyOther)
+ file.myOther = request.myOther.isNull() ? QString("") : request.myOther;
+
+ file.myUpdate = QDateTime::currentDateTime();
+
+ db->setFile(file);
+
+ qDebug() << "Clearing update_id" << id;
+ db->clearPendingMyListUpdate(request);
+ db->commit();
}
} // namespace LocalMyList
signals:
void batchFinished();
+ void myListUpdateBatchFinished();
public slots:
void handleRequests();
+ void handleMyListUpdates();
void animeRequestComplete(bool success);
void episodeRequestComplete(bool success);
void fileRequestComplete(bool success);
void voteRequestComplete(bool);
void myListAddReplyRecieved(bool success);
+ void myListEditReplyRecieved(bool success);
private:
Database *db;
QMap< ::AniDBUdpClient::VoteReply *, int> idMap;
+ QMap< ::AniDBUdpClient::MyListAddReply *, qint64> myListUpdateIdMap;
};
} // namespace LocalMyList
my_vote numeric(4,2),
my_vote_date timestamp without time zone,
my_temp_vote numeric(4,2),
- my_temp_vote_date timestamp without time zone
+ my_temp_vote_date timestamp without time zone,
+ CONSTRAINT aid_pk PRIMARY KEY (aid)
);
-ALTER TABLE ONLY anime ADD CONSTRAINT aid_pk PRIMARY KEY (aid);
CREATE INDEX rating_idx ON anime USING btree (rating);
CREATE INDEX temp_rating_idx ON anime USING btree (temp_rating);
CREATE INDEX my_vote_idx ON anime USING btree (my_vote);
aid integer NOT NULL,
type integer DEFAULT 1,
language character(8) DEFAULT ''::bpchar,
- title character varying(500) NOT NULL
+ title character varying(500) NOT NULL,
+ CONSTRAINT unique_title UNIQUE (aid, type, language, title)
);
-ALTER TABLE ONLY anime_title ADD CONSTRAINT unique_title UNIQUE (aid, type, language, title);
CREATE INDEX aid_idx ON anime_title USING btree (aid);
CREATE INDEX title_idx ON anime_title USING gin (to_tsvector('simple'::regconfig, (title)::text));
CREATE INDEX language_idx ON anime_title USING hash (language);
rating numeric(4,2),
votes integer,
my_vote numeric(4,2),
- my_vote_date timestamp without time zone
+ my_vote_date timestamp without time zone,
+ CONSTRAINT eid_pk PRIMARY KEY (eid)
);
-ALTER TABLE ONLY episode ADD CONSTRAINT eid_pk PRIMARY KEY (eid);
CREATE INDEX episode_aid_fk ON episode USING btree (aid);
CREATE TABLE file (
my_file_state integer,
my_storage text,
my_source text,
- my_other text
+ my_other text,
+ CONSTRAINT fid_pk PRIMARY KEY (fid)
);
-ALTER TABLE ONLY file ADD CONSTRAINT fid_pk PRIMARY KEY (fid);
CREATE INDEX file_aid_fk ON file USING btree (aid);
CREATE INDEX file_eid_fk ON file USING btree (eid);
fid integer NOT NULL,
eid integer NOT NULL,
start_percent integer,
- end_percent integer
+ end_percent integer,
+ CONSTRAINT fid_eid_pk PRIMARY KEY (fid, eid)
);
-ALTER TABLE ONLY file_episode_rel ADD CONSTRAINT fid_eid_pk PRIMARY KEY (fid, eid);
CREATE INDEX file_episode_rel_eid_fk ON file_episode_rel USING btree (eid);
CREATE INDEX file_episode_rel_fid_fk ON file_episode_rel USING btree (fid);
ed2k character(32) NOT NULL,
size bigint NOT NULL,
host_id integer,
- path text
+ path text,
+ CONSTRAINT unknown_files_pk PRIMARY KEY (ed2k, size)
);
-ALTER TABLE ONLY unknown_file ADD CONSTRAINT unknown_files_pk PRIMARY KEY (ed2k, size);
-CREATE TABLE pending_mylist_update (
- fid integer NOT NULL,
- my_watched timestamp without time zone,
- my_state integer,
- my_file_state integer,
- my_storage text,
- my_source text,
- my_other text,
- added timestamp without time zone DEFAULT now(),
- start timestamp without time zone
+CREATE TABLE pending_mylist_update
+(
+ update_id bigserial NOT NULL,
+ fid integer NOT NULL,
+ set_my_watched boolean NOT NULL DEFAULT false,
+ my_watched timestamp without time zone,
+ set_my_state boolean NOT NULL DEFAULT false,
+ my_state integer,
+ set_my_file_state boolean NOT NULL DEFAULT false,
+ my_file_state integer,
+ set_my_storage boolean NOT NULL DEFAULT false,
+ my_storage text,
+ set_my_source boolean NOT NULL DEFAULT false,
+ my_source text,
+ set_my_other boolean NOT NULL DEFAULT false,
+ my_other text,
+ added timestamp without time zone DEFAULT now(),
+ started timestamp without time zone,
+ finished timestamp without time zone,
+ CONSTRAINT pending_mylist_update_pk PRIMARY KEY (update_id )
);
-ALTER TABLE ONLY pending_mylist_update ADD CONSTRAINT pending_mylist_update_pk PRIMARY KEY (fid);
CREATE TABLE pending_request (
aid integer DEFAULT 0 NOT NULL,
size bigint DEFAULT 0 NOT NULL,
priority integer DEFAULT 1 NOT NULL,
added timestamp without time zone DEFAULT now(),
- start timestamp without time zone
+ start timestamp without time zone,
+ CONSTRAINT pending_request_pk PRIMARY KEY (aid, eid, fid, ed2k, size)
);
-ALTER TABLE ONLY pending_request ADD CONSTRAINT pending_request_pk PRIMARY KEY (aid, eid, fid, ed2k, size);
CREATE INDEX pending_request_added_idx ON pending_request USING btree (added);
CREATE INDEX pending_request_priority_idx ON pending_request USING btree (priority, added, start);
CREATE INDEX pending_request_start_idx ON pending_request USING btree (start);
CREATE TABLE config (
key character varying(250) NOT NULL,
value text,
- is_user_facing boolean DEFAULT false NOT NULL
+ is_user_facing boolean DEFAULT false NOT NULL,
+ CONSTRAINT config_pk PRIMARY KEY (key)
);
-ALTER TABLE ONLY config ADD CONSTRAINT config_pk PRIMARY KEY (key);
-
CREATE TABLE host (
host_id serial NOT NULL,
name character varying(100),
- is_udp_host boolean DEFAULT false
+ is_udp_host boolean DEFAULT false,
+ CONSTRAINT host_pk PRIMARY KEY (host_id),
+ CONSTRAINT host_unique_name UNIQUE (name)
);
-ALTER TABLE ONLY host ADD CONSTRAINT host_pk PRIMARY KEY (host_id);
-ALTER TABLE ONLY host ADD CONSTRAINT host_unique_name UNIQUE (name);
CREATE TABLE log (
log_id serial NOT NULL,
type integer,
- log text
+ log text,
+ CONSTRAINT log_pk PRIMARY KEY (log_id)
);
-ALTER TABLE ONLY log ADD CONSTRAINT log_pk PRIMARY KEY (log_id);
CREATE VIEW file_data AS
SELECT f.fid, f.eid, f.aid, f.gid, f.anidb_update, f.entry_update, f.my_update, f.ed2k, f.size, f.length, f.extension, f.group_name, f.group_name_short, f.crc, f.release_date, f.version, f.censored, f.type, f.quality_id, f.quality, f.resolution, f.video_codec, f.audio_codec, f.audio_language, f.subtitle_language, f.aspect_ratio, f.my_watched, f.my_state, f.my_file_state, f.my_storage, f.my_source, f.my_other, a.title_romaji AS atitle, e.title_english AS eptitle FROM ((file f LEFT JOIN anime a ON ((f.aid = a.aid))) LEFT JOIN episode e ON ((f.eid = e.eid)));
CREATE RULE unknown_file_ignore_duplicate AS ON INSERT TO unknown_file WHERE (EXISTS (SELECT 1 FROM unknown_file WHERE ((unknown_file.ed2k = new.ed2k) AND (unknown_file.size = new.size)))) DO INSTEAD NOTHING;
COMMENT ON RULE unknown_file_ignore_duplicate ON unknown_file IS 'Adding the same file more than once can happen';
+CREATE RULE new_pending_mylist_update_rule AS ON INSERT TO pending_mylist_update DO NOTIFY new_pending_mylist_update;
\ No newline at end of file