Refactor into a 'Source'-based structure
This commit is contained in:
parent
b2d74a8760
commit
d65c834a6f
1
Makefile
1
Makefile
|
@ -35,7 +35,6 @@ SRCS = \
|
|||
src/Feed.cpp \
|
||||
src/FeedEditWindow.cpp \
|
||||
src/FeedListItem.cpp \
|
||||
src/FeedSource.cpp \
|
||||
src/FeedsView.cpp \
|
||||
src/LocalSource.cpp \
|
||||
src/MainWindow.cpp \
|
||||
|
|
|
@ -29,17 +29,14 @@ APP_MIME_SIG = application/x-vnd.PoggerDaemon
|
|||
# same name (source.c or source.cpp) are included from different directories.
|
||||
# Also note that spaces in folder names do not work well with this Makefile.
|
||||
SRCS = \
|
||||
src/AtomFeed.cpp \
|
||||
src/Daemon.cpp \
|
||||
src/DeskbarView.cpp \
|
||||
src/Entry.cpp \
|
||||
src/Feed.cpp \
|
||||
src/FeedController.cpp \
|
||||
src/FeedSource.cpp \
|
||||
src/LocalSource.cpp \
|
||||
src/Notifier.cpp \
|
||||
src/Preferences.cpp \
|
||||
src/RssFeed.cpp \
|
||||
src/Util.cpp
|
||||
|
||||
# Specify the resource definition files to use. Full or relative paths can be
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <StorageKit.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "AtomFeed.h"
|
||||
#include "Entry.h"
|
||||
#include "Feed.h"
|
||||
#include "FeedController.h"
|
||||
|
@ -20,7 +19,6 @@
|
|||
#include "Mimetypes.h"
|
||||
#include "Notifier.h"
|
||||
#include "Preferences.h"
|
||||
#include "RssFeed.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
|
@ -158,7 +156,9 @@ App::_OpenSourceFile(BMessage* refMessage)
|
|||
{
|
||||
entry_ref entryRef;
|
||||
refMessage->FindRef("refs", &entryRef);
|
||||
FeedEditWindow* window = new FeedEditWindow(BEntry(&entryRef));
|
||||
BPath path(&entryRef);
|
||||
|
||||
FeedEditWindow* window = new FeedEditWindow(BString(path.Path()));
|
||||
}
|
||||
|
||||
|
||||
|
|
140
src/AtomFeed.cpp
140
src/AtomFeed.cpp
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include "AtomFeed.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <Catalog.h>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include "App.h"
|
||||
#include "Entry.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
#define B_TRANSLATION_CONTEXT "AtomFeed"
|
||||
|
||||
|
||||
AtomFeed::AtomFeed()
|
||||
{
|
||||
fTitle = BString("");
|
||||
fCachePath = BString("");
|
||||
}
|
||||
|
||||
|
||||
AtomFeed::AtomFeed(Feed* feed)
|
||||
: AtomFeed::AtomFeed()
|
||||
{
|
||||
SetTitle(feed->Title());
|
||||
SetXmlUrl(feed->XmlUrl());
|
||||
SetCachePath(feed->CachePath());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AtomFeed::Parse()
|
||||
{
|
||||
fEntries = BObjectList<Entry>(5, true);
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(CachePath().String());
|
||||
|
||||
Feed::Parse();
|
||||
|
||||
tinyxml2::XMLElement* xfeed = xml.FirstChildElement("feed");
|
||||
|
||||
RootParse(xfeed);
|
||||
ParseEntries(xfeed);
|
||||
|
||||
Filetize();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AtomFeed::RootParse(tinyxml2::XMLElement* xfeed)
|
||||
{
|
||||
tinyxml2::XMLElement* xentry = xfeed->FirstChildElement("entry");
|
||||
|
||||
bool set = false;
|
||||
|
||||
_SetTitle(xfeed->FirstChildElement("title"));
|
||||
|
||||
set = _SetDate(xfeed->FirstChildElement("updated"));
|
||||
if (!set)
|
||||
set = _SetDate(xfeed->FirstChildElement("published"));
|
||||
if (!set && xentry)
|
||||
set = _SetDate(xentry->FirstChildElement("updated"));
|
||||
if (!set && xentry)
|
||||
set = _SetDate(xentry->FirstChildElement("published"));
|
||||
|
||||
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
||||
logString.ReplaceAll("%source%", fTitle.String());
|
||||
logString.ReplaceAll("%url%", fXmlUrl.UrlString());
|
||||
|
||||
std::cout << logString.String();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AtomFeed::EntryParse(tinyxml2::XMLElement* xentry)
|
||||
{
|
||||
Entry* newEntry = new Entry();
|
||||
|
||||
tinyxml2::XMLElement* xcontent = xentry->FirstChildElement("content");
|
||||
tinyxml2::XMLElement* xmedia = xentry->FirstChildElement("media:group");
|
||||
tinyxml2::XMLPrinter xprinter;
|
||||
|
||||
newEntry->SetTitle(xentry->FirstChildElement("title"));
|
||||
newEntry->SetPostUrl(xentry->FirstChildElement("link")->Attribute("href"));
|
||||
newEntry->SetFeedTitle(fTitle);
|
||||
|
||||
bool set = false;
|
||||
set = newEntry->SetDescription(xentry->FirstChildElement("summary"));
|
||||
if (!set)
|
||||
set = newEntry->SetDescription(xentry->FirstChildElement("description"));
|
||||
if (!set && xmedia)
|
||||
set = newEntry->SetDescription(
|
||||
xmedia->FirstChildElement("media:description"));
|
||||
|
||||
set = newEntry->SetDate(xentry->FirstChildElement("updated"));
|
||||
if (!set)
|
||||
set = newEntry->SetDate(xentry->FirstChildElement("published"));
|
||||
|
||||
if (fDate == NULL || fDate < newEntry->Date())
|
||||
_SetDate(newEntry->Date());
|
||||
|
||||
if (xcontent) {
|
||||
xcontent->Accept(&xprinter);
|
||||
newEntry->SetContent(xprinter.CStr());
|
||||
}
|
||||
|
||||
_AddEntry(newEntry);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AtomFeed::ParseEntries(tinyxml2::XMLElement* xfeed)
|
||||
{
|
||||
tinyxml2::XMLElement* xentry;
|
||||
|
||||
xentry = xfeed->FirstChildElement("entry");
|
||||
|
||||
int entryCount = _XmlCountSiblings(xentry, "entry");
|
||||
fEntries = BObjectList<Entry>(entryCount, true);
|
||||
|
||||
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
||||
BString entryStr;
|
||||
entryStr << entryCount;
|
||||
logString.ReplaceAll("%count%", entryStr);
|
||||
|
||||
std::cout << logString.String();
|
||||
|
||||
while (xentry) {
|
||||
EntryParse(xentry);
|
||||
xentry = xentry->NextSiblingElement("entry");
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef ATOM_FEED_H
|
||||
#define ATOM_FEED_H
|
||||
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include "Feed.h"
|
||||
|
||||
|
||||
class AtomFeed: public Feed {
|
||||
public:
|
||||
AtomFeed();
|
||||
AtomFeed(Feed*);
|
||||
|
||||
void Parse();
|
||||
void RootParse(tinyxml2::XMLElement*);
|
||||
void EntryParse(tinyxml2::XMLElement*);
|
||||
void ParseEntries(tinyxml2::XMLElement*);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -192,24 +192,10 @@ Entry::Date()
|
|||
|
||||
|
||||
bool
|
||||
Entry::SetDate(const char* dateStr)
|
||||
Entry::SetDate(BDateTime date)
|
||||
{
|
||||
if (dateStr == NULL)
|
||||
return false;
|
||||
BDateTime newDate = feedDateToBDate(dateStr);
|
||||
if (newDate == NULL)
|
||||
return false;
|
||||
fDate = newDate;
|
||||
fDate = date;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Entry::SetDate(tinyxml2::XMLElement* elem)
|
||||
{
|
||||
if (elem != NULL)
|
||||
return SetDate(elem->GetText());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ public:
|
|||
bool SetPostUrl(tinyxml2::XMLElement*);
|
||||
|
||||
BDateTime Date();
|
||||
bool SetDate(const char*);
|
||||
bool SetDate(tinyxml2::XMLElement*);
|
||||
|
||||
bool SetDate(BDateTime date);
|
||||
|
||||
private:
|
||||
BString fTitle;
|
||||
|
|
328
src/Feed.cpp
328
src/Feed.cpp
|
@ -14,69 +14,18 @@
|
|||
#include "Util.h"
|
||||
|
||||
|
||||
Feed::Feed(BUrl xml, BString path)
|
||||
: Feed()
|
||||
Feed::Feed()
|
||||
: fTitle(BString(""))
|
||||
{
|
||||
SetXmlUrl(xml);
|
||||
SetCachePath(path);
|
||||
fLastDate = BDateTime::CurrentDateTime(B_LOCAL_TIME);
|
||||
}
|
||||
|
||||
|
||||
Feed::Feed(BUrl xml)
|
||||
: Feed()
|
||||
Feed::Feed(const char* identifier, const char* title, const char* url)
|
||||
: fTitle(BString(title)),
|
||||
fIdentifier(BString(identifier)),
|
||||
fUrl(BUrl(url))
|
||||
{
|
||||
SetXmlUrl(xml);
|
||||
|
||||
BPath cache;
|
||||
find_directory(B_USER_CACHE_DIRECTORY, &cache);
|
||||
cache.Append("Pogger");
|
||||
|
||||
cache.Append(urlToFilename(fXmlUrl));
|
||||
SetCachePath(cache.Path());
|
||||
}
|
||||
|
||||
|
||||
// For pre-existing feed
|
||||
Feed::Feed(BEntry entry)
|
||||
: Feed()
|
||||
{
|
||||
BFile file(&entry, B_READ_ONLY);
|
||||
BPath path;
|
||||
entry.GetPath(&path);
|
||||
SetCachePath(BString(path.Path()));
|
||||
|
||||
BString name, url;
|
||||
file.ReadAttrString("META:url", &url);
|
||||
file.ReadAttrString("Feed:name", &name);
|
||||
file.ReadAttrString("Feed:hash", &fLastHash);
|
||||
|
||||
if (!url.IsEmpty())
|
||||
SetXmlUrl(BUrl(url));
|
||||
if (!name.IsEmpty())
|
||||
SetTitle(name);
|
||||
}
|
||||
|
||||
|
||||
Feed::Feed(const char* pathStr)
|
||||
: Feed(BEntry(pathStr))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// For new feed
|
||||
Feed::Feed(BUrl xml, BEntry entry)
|
||||
: Feed()
|
||||
{
|
||||
BPath path;
|
||||
BString pathString;
|
||||
entry.GetPath(&path);
|
||||
pathString = path.Path();
|
||||
|
||||
if (entry.IsDirectory())
|
||||
pathString += BString(urlToFilename(xml));
|
||||
|
||||
SetCachePath(pathString);
|
||||
SetXmlUrl(xml);
|
||||
}
|
||||
|
||||
|
||||
|
@ -84,128 +33,10 @@ Feed::Feed(Feed* feed)
|
|||
: Feed()
|
||||
{
|
||||
SetTitle(feed->Title());
|
||||
SetXmlUrl(feed->XmlUrl());
|
||||
}
|
||||
|
||||
|
||||
Feed::Feed()
|
||||
:
|
||||
fTitle(BString(""))
|
||||
{
|
||||
fLastDate = BDateTime::CurrentDateTime(B_LOCAL_TIME);
|
||||
SetUrl(feed->Url());
|
||||
}
|
||||
|
||||
|
||||
Feed::~Feed()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Feed::Parse()
|
||||
{
|
||||
BFile feedFile = BFile(fCachePath, B_READ_ONLY);
|
||||
time_t tt_lastDate = 0;
|
||||
|
||||
feedFile.ReadAttr("Feed:when", B_TIME_TYPE, 0, &tt_lastDate, sizeof(time_t));
|
||||
|
||||
if (tt_lastDate > 0)
|
||||
fLastDate.SetTime_t(tt_lastDate);
|
||||
}
|
||||
|
||||
|
||||
// Download a remote feed's XML to the cache path.
|
||||
bool
|
||||
Feed::Fetch()
|
||||
{
|
||||
BFile cacheFile = BFile(fCachePath, B_READ_WRITE | B_CREATE_FILE);
|
||||
|
||||
int32 result = fetch(fXmlUrl, &cacheFile, &fHash, 30);
|
||||
cacheFile.WriteAttrString("Feed:hash", &fHash);
|
||||
|
||||
if (result == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Feed::Filetize()
|
||||
{
|
||||
BFile feedFile(fCachePath, B_WRITE_ONLY | B_CREATE_FILE);
|
||||
time_t tt_date = fDate.Time_t();
|
||||
BString url = fXmlUrl.UrlString();
|
||||
BString name = Title();
|
||||
BString type("application/x-feed-source");
|
||||
|
||||
feedFile.WriteAttrString("Feed:name", &name);
|
||||
feedFile.WriteAttrString("META:url", &url);
|
||||
feedFile.WriteAttrString("BEOS:TYPE", &type);
|
||||
feedFile.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t));
|
||||
feedFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(),
|
||||
type.CountChars() + 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Feed::Unfiletize()
|
||||
{
|
||||
BEntry entry(CachePath().String());
|
||||
entry.Remove();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::IsRss()
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(fCachePath.String());
|
||||
|
||||
if (xml.FirstChildElement("rss"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::IsAtom()
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(fCachePath.String());
|
||||
|
||||
if (xml.FirstChildElement("feed"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::IsUpdated()
|
||||
{
|
||||
return fLastHash != fHash;
|
||||
}
|
||||
|
||||
|
||||
// Count the amount of siblings to an element of given type name
|
||||
int
|
||||
Feed::_XmlCountSiblings (tinyxml2::XMLElement* xsibling, const char* sibling_name)
|
||||
{
|
||||
int count = 0;
|
||||
while (xsibling) {
|
||||
count++;
|
||||
xsibling = xsibling->NextSiblingElement(sibling_name);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::_AddEntry (Entry* newEntry)
|
||||
{
|
||||
fEntries.AddItem(newEntry);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BObjectList<Entry>
|
||||
Feed::Entries()
|
||||
|
@ -228,22 +59,10 @@ Feed::NewEntries()
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::SetTitle(const char* titleStr)
|
||||
void
|
||||
Feed::SetEntries(BObjectList<Entry> entries)
|
||||
{
|
||||
if (titleStr != NULL)
|
||||
fTitle = BString(titleStr);
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::_SetTitle(tinyxml2::XMLElement* elem)
|
||||
{
|
||||
if (elem != NULL && fTitle.IsEmpty() == true)
|
||||
return SetTitle(elem->GetText());
|
||||
else return false;
|
||||
fEntries = entries;
|
||||
}
|
||||
|
||||
|
||||
|
@ -255,65 +74,30 @@ Feed::Title()
|
|||
|
||||
|
||||
bool
|
||||
Feed::SetXmlUrl(BUrl newUrl)
|
||||
Feed::SetTitle(const char* titleStr)
|
||||
{
|
||||
fXmlUrl = newUrl;
|
||||
if (titleStr != NULL)
|
||||
fTitle = BString(titleStr);
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BUrl
|
||||
Feed::XmlUrl()
|
||||
Feed::Url()
|
||||
{
|
||||
return fXmlUrl;
|
||||
return fUrl;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::SetCachePath(BString path)
|
||||
Feed::SetUrl(BUrl newUrl)
|
||||
{
|
||||
fCachePath = path;
|
||||
fUrl = newUrl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
Feed::CachePath()
|
||||
{
|
||||
return fCachePath;
|
||||
}
|
||||
|
||||
|
||||
// Set the latest date given by feed (from entry or from root)
|
||||
bool
|
||||
Feed::_SetDate(BDateTime newDate)
|
||||
{
|
||||
if (newDate == NULL)
|
||||
return false;
|
||||
fDate = newDate;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::_SetDate(const char* dateCStr)
|
||||
{
|
||||
if (dateCStr == NULL)
|
||||
return false;
|
||||
_SetDate(feedDateToBDate(dateCStr));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::_SetDate(tinyxml2::XMLElement* elem)
|
||||
{
|
||||
if (elem != NULL)
|
||||
return _SetDate(elem->GetText());
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
||||
BDateTime
|
||||
Feed::Date()
|
||||
{
|
||||
|
@ -321,3 +105,77 @@ Feed::Date()
|
|||
}
|
||||
|
||||
|
||||
// Set the latest date given by feed (from entry or from metadata)
|
||||
bool
|
||||
Feed::SetDate(BDateTime newDate)
|
||||
{
|
||||
if (newDate == NULL)
|
||||
return false;
|
||||
fDate = newDate;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BDateTime
|
||||
Feed::LastDate()
|
||||
{
|
||||
return fLastDate;
|
||||
}
|
||||
|
||||
|
||||
// Set the latest time the feed was fetched and parsed
|
||||
bool
|
||||
Feed::SetLastDate(BDateTime newDate)
|
||||
{
|
||||
if (newDate == NULL)
|
||||
return false;
|
||||
fLastDate = newDate;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
Feed::Hash()
|
||||
{
|
||||
return fHash;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::SetHash(BString hash)
|
||||
{
|
||||
fHash = hash;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
Feed::LastHash()
|
||||
{
|
||||
return fLastHash;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::SetLastHash(BString hash)
|
||||
{
|
||||
fLastHash = hash;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BString
|
||||
Feed::Identifier()
|
||||
{
|
||||
return fIdentifier;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Feed::SetIdentifier(BString id)
|
||||
{
|
||||
fIdentifier = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
59
src/Feed.h
59
src/Feed.h
|
@ -21,57 +21,42 @@ class BUrl;
|
|||
|
||||
class Feed {
|
||||
public:
|
||||
Feed(BUrl);
|
||||
Feed(BUrl, BString);
|
||||
Feed(BEntry);
|
||||
Feed(const char* pathStr);
|
||||
Feed(BUrl xml, BEntry entry);
|
||||
Feed(Feed*);
|
||||
Feed();
|
||||
|
||||
~Feed();
|
||||
|
||||
virtual void Parse();
|
||||
Feed(const char* identifier, const char* title, const char* url);
|
||||
Feed(Feed*);
|
||||
|
||||
BObjectList<Entry> Entries();
|
||||
BObjectList<Entry> NewEntries();
|
||||
|
||||
bool Fetch();
|
||||
|
||||
void Filetize();
|
||||
void Unfiletize();
|
||||
|
||||
bool IsRss();
|
||||
bool IsAtom();
|
||||
bool IsUpdated();
|
||||
void SetEntries(BObjectList<Entry> entries);
|
||||
|
||||
BString Title();
|
||||
bool SetTitle(const char*);
|
||||
|
||||
BUrl Url();
|
||||
bool SetUrl(BUrl newUrl);
|
||||
|
||||
BDateTime Date();
|
||||
bool SetDate(BDateTime);
|
||||
|
||||
BUrl XmlUrl();
|
||||
BDateTime Date();
|
||||
BDateTime LastDate();
|
||||
bool SetLastDate(BDateTime);
|
||||
|
||||
bool SetTitle(const char*);
|
||||
bool SetXmlUrl(BUrl newUrl);
|
||||
bool SetCachePath(BString path);
|
||||
BString Hash();
|
||||
bool SetHash(BString hash);
|
||||
|
||||
BString CachePath();
|
||||
BString LastHash();
|
||||
bool SetLastHash(BString hash);
|
||||
|
||||
BString Identifier();
|
||||
bool SetIdentifier(BString id);
|
||||
|
||||
protected:
|
||||
bool _SetTitle(tinyxml2::XMLElement*);
|
||||
bool _SetDate(const char*);
|
||||
bool _SetDate(tinyxml2::XMLElement*);
|
||||
bool _SetDate(BDateTime newDate);
|
||||
|
||||
bool _AddEntry(Entry*);
|
||||
|
||||
int _XmlCountSiblings(tinyxml2::XMLElement*, const char*);
|
||||
|
||||
BString fTitle;
|
||||
BDateTime fDate;
|
||||
BDateTime fLastDate;
|
||||
BUrl fXmlUrl;
|
||||
BString fCachePath;
|
||||
BDateTime fDate; // Lastest date (of post/update) given by feed
|
||||
BDateTime fLastDate; // Last time feed was parsed
|
||||
BUrl fUrl;
|
||||
BString fIdentifier;
|
||||
BString fHash;
|
||||
BString fLastHash;
|
||||
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
#include <StringList.h>
|
||||
|
||||
#include "Daemon.h"
|
||||
#include "AtomFeed.h"
|
||||
#include "Entry.h"
|
||||
#include "LocalSource.h"
|
||||
#include "RssFeed.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
|
@ -32,7 +30,7 @@ FeedController::FeedController()
|
|||
fMainThread(find_thread(NULL)),
|
||||
fDownloadThread(0),
|
||||
fParseThread(0),
|
||||
fDownloadQueue(new BStringList()),
|
||||
fDownloadQueue(new BObjectList<Feed>()),
|
||||
fMessageRunner(new BMessageRunner(be_app, BMessage(kControllerCheck), 50000, -1))
|
||||
|
||||
{
|
||||
|
@ -61,21 +59,20 @@ FeedController::MessageReceived(BMessage* msg)
|
|||
{
|
||||
int i = 0;
|
||||
const void* data;
|
||||
ssize_t size = sizeof(BString);
|
||||
|
||||
BStringList paths;
|
||||
msg->FindStrings("feedPaths", &paths);
|
||||
fDownloadQueue->Add(paths);
|
||||
ssize_t size = sizeof(Feed);
|
||||
|
||||
while (msg->HasData("feeds", B_RAW_TYPE, i)) {
|
||||
msg->FindData("feeds", B_RAW_TYPE, i, &data, &size);
|
||||
fDownloadQueue->AddItem((Feed*)data);
|
||||
_SendProgress();
|
||||
}
|
||||
fMessageRunner->SetCount(-1);
|
||||
_SendProgress();
|
||||
break;
|
||||
}
|
||||
case kUpdateSubscribed:
|
||||
{
|
||||
BStringList subFeeds = LocalSource::Feeds();
|
||||
fDownloadQueue->Add(subFeeds);
|
||||
|
||||
BObjectList<Feed> list = LocalSource::Feeds();
|
||||
fDownloadQueue->AddList(&list);
|
||||
_SendProgress();
|
||||
break;
|
||||
}
|
||||
|
@ -97,7 +94,7 @@ FeedController::MessageReceived(BMessage* msg)
|
|||
void
|
||||
FeedController::_SendProgress()
|
||||
{
|
||||
int32 dqCount = fDownloadQueue->CountStrings();
|
||||
int32 dqCount = fDownloadQueue->CountItems();
|
||||
|
||||
if (fEnqueuedTotal < dqCount)
|
||||
fEnqueuedTotal = dqCount;
|
||||
|
@ -116,19 +113,13 @@ void
|
|||
FeedController::_ProcessQueueItem()
|
||||
{
|
||||
if (has_data(fDownloadThread) && !fDownloadQueue->IsEmpty()) {
|
||||
BString feed = fDownloadQueue->Remove(0);
|
||||
Feed* buffer;
|
||||
|
||||
if (BUrl(feed.String()).IsValid() == true)
|
||||
buffer = new Feed(BUrl(feed.String()));
|
||||
else
|
||||
buffer = new Feed(feed.String());
|
||||
|
||||
send_data(fDownloadThread, 0, (void*)buffer, sizeof(Feed));
|
||||
Feed* feed = fDownloadQueue->ItemAt(0);
|
||||
fDownloadQueue->RemoveItemAt(0);
|
||||
send_data(fDownloadThread, 0, (void*)feed, sizeof(Feed));
|
||||
|
||||
BMessage downloadInit = BMessage(kDownloadStart);
|
||||
downloadInit.AddString("feed_name", buffer->Title());
|
||||
downloadInit.AddString("feed_url", buffer->XmlUrl().UrlString());
|
||||
downloadInit.AddString("feed_name", feed->Title());
|
||||
downloadInit.AddString("feed_url", feed->Url().UrlString());
|
||||
((App*)be_app)->MessageReceived(&downloadInit);
|
||||
}
|
||||
}
|
||||
|
@ -149,8 +140,7 @@ FeedController::_ReceiveStatus()
|
|||
{
|
||||
BMessage complete = BMessage(kDownloadComplete);
|
||||
complete.AddString("feed_name", feedBuffer->Title());
|
||||
complete.AddString("feed_url",
|
||||
feedBuffer->XmlUrl().UrlString());
|
||||
complete.AddString("feed_url", feedBuffer->Url().UrlString());
|
||||
((App*)be_app)->MessageReceived(&complete);
|
||||
|
||||
send_data(fParseThread, 0, (void*)feedBuffer, sizeof(Feed));
|
||||
|
@ -161,7 +151,7 @@ FeedController::_ReceiveStatus()
|
|||
BMessage failure = BMessage(kDownloadFail);
|
||||
failure.AddString("feed_name", feedBuffer->Title());
|
||||
failure.AddString("feed_url",
|
||||
feedBuffer->XmlUrl().UrlString());
|
||||
feedBuffer->Url().UrlString());
|
||||
((App*)be_app)->MessageReceived(&failure);
|
||||
_SendProgress();
|
||||
break;
|
||||
|
@ -170,7 +160,7 @@ FeedController::_ReceiveStatus()
|
|||
{
|
||||
BMessage failure = BMessage(kParseFail);
|
||||
failure.AddString("feed_name", feedBuffer->Title());
|
||||
failure.AddString("feed_url", feedBuffer->XmlUrl().UrlString());
|
||||
failure.AddString("feed_url", feedBuffer->Url().UrlString());
|
||||
((App*)be_app)->MessageReceived(&failure);
|
||||
_SendProgress();
|
||||
break;
|
||||
|
@ -179,7 +169,7 @@ FeedController::_ReceiveStatus()
|
|||
default:
|
||||
BMessage complete = BMessage(kParseComplete);
|
||||
complete.AddString("feed_name", feedBuffer->Title());
|
||||
complete.AddString("feed_url", feedBuffer->XmlUrl().UrlString());
|
||||
complete.AddString("feed_url", feedBuffer->Url().UrlString());
|
||||
complete.AddInt32("entry_count", code);
|
||||
((App*)be_app)->MessageReceived(&complete);
|
||||
_SendProgress();
|
||||
|
@ -201,9 +191,9 @@ FeedController::_DownloadLoop(void* data)
|
|||
receive_data(&sender, (void*)feedBuffer, sizeof(Feed));
|
||||
|
||||
std::cout << B_TRANSLATE("Downloading feed from ")
|
||||
<< feedBuffer->XmlUrl().UrlString() << "…\n";
|
||||
<< feedBuffer->Url().UrlString() << "…\n";
|
||||
|
||||
if (feedBuffer->Fetch()) {
|
||||
if (LocalSource::Fetch(feedBuffer)) {
|
||||
send_data(main, kDownloadComplete, (void*)feedBuffer, sizeof(Feed));
|
||||
}
|
||||
else {
|
||||
|
@ -228,39 +218,19 @@ FeedController::_ParseLoop(void* data)
|
|||
BObjectList<Entry> entries;
|
||||
int32 entriesCount = 0;
|
||||
BString feedTitle;
|
||||
BUrl feedUrl = feedBuffer->XmlUrl();
|
||||
BUrl feedUrl = feedBuffer->Url();
|
||||
|
||||
if (feedBuffer->IsAtom() && feedBuffer->IsUpdated()) {
|
||||
AtomFeed feed(feedBuffer);
|
||||
feed.Parse();
|
||||
entries = feed.NewEntries();
|
||||
entriesCount = entries.CountItems();
|
||||
feedTitle = feed.Title();
|
||||
LocalSource::Parse(feedBuffer);
|
||||
entries = feedBuffer->NewEntries();
|
||||
entriesCount = entries.CountItems();
|
||||
feedTitle = feedBuffer->Title();
|
||||
|
||||
for (int i = 0; i < entriesCount; i++)
|
||||
entries.ItemAt(i)->Filetize();
|
||||
entries.MakeEmpty();
|
||||
}
|
||||
else if (feedBuffer->IsRss() && feedBuffer->IsUpdated()) {
|
||||
RssFeed feed(feedBuffer);
|
||||
feed.Parse();
|
||||
entries = feed.NewEntries();
|
||||
entriesCount = entries.CountItems();
|
||||
feedTitle = feed.Title();
|
||||
|
||||
for (int i = 0; i < entriesCount; i++)
|
||||
entries.ItemAt(i)->Filetize();
|
||||
entries.MakeEmpty();
|
||||
}
|
||||
|
||||
|
||||
if (feedBuffer->IsAtom() || feedBuffer->IsRss()) {
|
||||
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
||||
}
|
||||
else {
|
||||
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
||||
}
|
||||
for (int i = 0; i < entriesCount; i++)
|
||||
entries.ItemAt(i)->Filetize();
|
||||
entries.MakeEmpty();
|
||||
|
||||
LocalSource::EditFeed(feedBuffer);
|
||||
send_data(main, entriesCount, (void*)feedBuffer, sizeof(Feed));
|
||||
free(feedBuffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,6 @@ public:
|
|||
|
||||
void MessageReceived(BMessage* msg);
|
||||
|
||||
static BStringList SubscribedFeeds();
|
||||
|
||||
private:
|
||||
void _SendProgress();
|
||||
|
||||
|
@ -54,7 +52,7 @@ private:
|
|||
thread_id fDownloadThread;
|
||||
thread_id fParseThread;
|
||||
|
||||
BStringList* fDownloadQueue;
|
||||
BObjectList<Feed>* fDownloadQueue;
|
||||
BMessageRunner* fMessageRunner;
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "FeedController.h"
|
||||
#include "FeedListItem.h"
|
||||
#include "FeedsView.h"
|
||||
#include "LocalSource.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
|
@ -33,26 +34,18 @@ FeedEditWindow::FeedEditWindow()
|
|||
{
|
||||
_InitInterface();
|
||||
MoveOnScreen();
|
||||
fFeed = Feed();
|
||||
fFeed = new Feed();
|
||||
}
|
||||
|
||||
|
||||
FeedEditWindow::FeedEditWindow(BEntry feedEntry)
|
||||
:
|
||||
FeedEditWindow()
|
||||
FeedEditWindow::FeedEditWindow(BString identifier)
|
||||
: FeedEditWindow()
|
||||
{
|
||||
SetTitle(B_TRANSLATE("Edit feed"));
|
||||
fFeed = Feed(feedEntry);
|
||||
fFeed = LocalSource::GetFeed(identifier);
|
||||
|
||||
fFeedNameText->SetText(fFeed.Title().String());
|
||||
fFeedUrlText->SetText(fFeed.XmlUrl().UrlString().String());
|
||||
}
|
||||
|
||||
|
||||
FeedEditWindow::FeedEditWindow(FeedListItem* feedItem)
|
||||
:
|
||||
FeedEditWindow(BEntry(feedItem->FeedPath()))
|
||||
{
|
||||
fFeedNameText->SetText(fFeed->Title().String());
|
||||
fFeedUrlText->SetText(fFeed->Url().UrlString().String());
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,22 +121,6 @@ FeedEditWindow::_InitInterface()
|
|||
void
|
||||
FeedEditWindow::_SaveFeed()
|
||||
{
|
||||
BPath subPath;
|
||||
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
|
||||
subPath.Append("Pogger");
|
||||
subPath.Append("Subscriptions");
|
||||
BDirectory subDir(subPath.Path());
|
||||
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
||||
subDir.CreateDirectory(subPath.Path(), &subDir);
|
||||
|
||||
BPath defaultSubPath(subPath);
|
||||
defaultSubPath.Append("Haiku Project");
|
||||
Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
|
||||
BEntry(defaultSubPath.Path()));
|
||||
defaultSub.SetTitle("Haiku Project");
|
||||
defaultSub.Filetize();
|
||||
}
|
||||
|
||||
BString title(fFeedNameText->Text());
|
||||
const char* urlString = fFeedUrlText->Text();
|
||||
BUrl url = BUrl(urlString);
|
||||
|
@ -165,26 +142,20 @@ FeedEditWindow::_SaveFeed()
|
|||
return;
|
||||
}
|
||||
|
||||
BString filename;
|
||||
if (title.IsEmpty())
|
||||
filename = BString(urlToFilename(BUrl(urlString)));
|
||||
else
|
||||
filename = BString(title);
|
||||
subPath.Append(filename);
|
||||
|
||||
if (fFeed.CachePath().IsEmpty())
|
||||
fFeed.SetCachePath(BString(subPath.Path()));
|
||||
|
||||
if (!title.IsEmpty())
|
||||
fFeed.SetTitle(title.String());
|
||||
fFeed.SetXmlUrl(BUrl(urlString));
|
||||
fFeed.Filetize();
|
||||
fFeed->SetTitle(title.String());
|
||||
fFeed->SetUrl(BUrl(urlString));
|
||||
|
||||
if (fFeed->Identifier().IsEmpty() == true)
|
||||
LocalSource::AddFeed(fFeed);
|
||||
else
|
||||
LocalSource::EditFeed(fFeed);
|
||||
|
||||
BMessage edited(kFeedsEdited);
|
||||
BMessage enqueueUpdated(kEnqueueFeed);
|
||||
enqueueUpdated.AddString("feedPaths", fFeed.CachePath());
|
||||
|
||||
((App*)be_app)->MessageReceived(&enqueueUpdated);
|
||||
// BMessage enqueueUpdated(kEnqueueFeed);
|
||||
// enqueueUpdated.AddData("feeds", B_RAW_TYPE, &fFeed, sizeof(Feed));
|
||||
//
|
||||
// ((App*)be_app)->MessageReceived(&enqueueUpdated);
|
||||
((App*)be_app)->PostMessage(&edited);
|
||||
Quit();
|
||||
}
|
||||
|
|
|
@ -26,8 +26,7 @@ enum
|
|||
class FeedEditWindow : public BWindow {
|
||||
public:
|
||||
FeedEditWindow();
|
||||
FeedEditWindow(BEntry feedEntry);
|
||||
FeedEditWindow(FeedListItem* feedItem);
|
||||
FeedEditWindow(BString feedIdentifier);
|
||||
~FeedEditWindow();
|
||||
|
||||
void MessageReceived(BMessage* msg);
|
||||
|
@ -38,7 +37,7 @@ private:
|
|||
void _SaveFeed();
|
||||
|
||||
BString fFeedPath;
|
||||
Feed fFeed;
|
||||
Feed* fFeed;
|
||||
|
||||
BStringView* fFeedNameLabel;
|
||||
BTextControl* fFeedNameText;
|
||||
|
|
|
@ -20,8 +20,8 @@ FeedListItem::FeedListItem(Feed* feed)
|
|||
:
|
||||
BStringItem(feed->Title().String(), 0, false),
|
||||
fStatus(kClearStatus),
|
||||
fFeedUrl(feed->XmlUrl()),
|
||||
fFeedPath(feed->CachePath())
|
||||
fFeedUrl(feed->Url()),
|
||||
fFeedIdentifier(feed->Identifier())
|
||||
{
|
||||
if (feed->Title().IsEmpty() == true)
|
||||
SetText(B_TRANSLATE("Untitled Feed"));
|
||||
|
@ -59,9 +59,9 @@ FeedListItem::DrawItem(BView* owner, BRect frame, bool complete)
|
|||
|
||||
|
||||
BString
|
||||
FeedListItem::FeedPath()
|
||||
FeedListItem::FeedIdentifier()
|
||||
{
|
||||
return fFeedPath;
|
||||
return fFeedIdentifier;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,14 +29,14 @@ public:
|
|||
void DrawItem(BView* owner, BRect frame, bool complete);
|
||||
|
||||
BUrl FeedUrl();
|
||||
BString FeedPath();
|
||||
BString FeedIdentifier();
|
||||
|
||||
void SetStatus(int8 status);
|
||||
|
||||
private:
|
||||
int8 fStatus;
|
||||
BUrl fFeedUrl;
|
||||
BString fFeedPath;
|
||||
BString fFeedIdentifier;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
#include "FeedSource.h"
|
||||
|
|
@ -7,14 +7,17 @@
|
|||
|
||||
#include <StringList.h>
|
||||
|
||||
#include "Feed.h"
|
||||
|
||||
|
||||
// Hierarchy: FeedSource → Feed → Entry
|
||||
// TODO: RemoveFeed(), AddFeed(), etc.
|
||||
class FeedSource {
|
||||
public:
|
||||
BStringList Feeds();
|
||||
static BStringList FeedIdentifiers();
|
||||
|
||||
const char* name = "";
|
||||
Feed RemoveFeed(Feed feed);
|
||||
Feed RemoveFeed(const char* identifier);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ FeedsView::_EditSelectedFeed()
|
|||
{
|
||||
int32 selIndex = fFeedsListView->CurrentSelection();
|
||||
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
||||
FeedEditWindow* edit = new FeedEditWindow(selected);
|
||||
FeedEditWindow* edit = new FeedEditWindow(selected->FeedIdentifier());
|
||||
|
||||
edit->Show();
|
||||
edit->Activate();
|
||||
|
@ -211,24 +211,23 @@ FeedsView::_RemoveSelectedFeed()
|
|||
|
||||
int32 selIndex = fFeedsListView->CurrentSelection();
|
||||
FeedListItem* selected = (FeedListItem*)fFeedsListView->ItemAt(selIndex);
|
||||
Feed delFeed = Feed(BEntry(selected->FeedPath()));
|
||||
|
||||
delFeed.Unfiletize();
|
||||
LocalSource::RemoveFeed(LocalSource::GetFeed(selected->FeedIdentifier()));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FeedsView::_PopulateFeedList()
|
||||
{
|
||||
BStringList feeds = LocalSource::Feeds();
|
||||
BObjectList<Feed> feeds = LocalSource::Feeds();
|
||||
int32 selected = fFeedsListView->CurrentSelection();
|
||||
|
||||
for (int i = fFeedsListView->CountItems(); i >= 0; i--)
|
||||
delete ((FeedListItem*)fFeedsListView->RemoveItem(i));
|
||||
|
||||
for (int i = 0; i < feeds.CountStrings(); i++) {
|
||||
Feed feed = Feed(feeds.StringAt(i).String());
|
||||
FeedListItem* item = new FeedListItem(&feed);
|
||||
for (int i = 0; i < feeds.CountItems(); i++) {
|
||||
Feed* feed = feeds.ItemAt(i);
|
||||
FeedListItem* item = new FeedListItem(feed);
|
||||
fFeedsListView->AddItem(item);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,40 +5,432 @@
|
|||
|
||||
#include "LocalSource.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <Catalog.h>
|
||||
#include <Directory.h>
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include "Feed.h"
|
||||
#include "Util.h"
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
#define B_TRANSLATION_CONTEXT "LocalSource"
|
||||
|
||||
|
||||
BStringList
|
||||
LocalSource::LocalSource()
|
||||
{
|
||||
BDirectory subDir = BDirectory(_SubscriptionPath().Path());
|
||||
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
||||
// subDir.CreateDirectory(subPath.Path(), &subDir);
|
||||
|
||||
// subPath.Append("Haiku Project");
|
||||
// Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
|
||||
// BEntry(subPath.Path()));
|
||||
// defaultSub.SetTitle("Haiku Project");
|
||||
// defaultSub.Filetize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BObjectList<Feed>
|
||||
LocalSource::Feeds()
|
||||
{
|
||||
BDirectory subDir = BDirectory(_SubscriptionPath().Path());
|
||||
BEntry feedEntry;
|
||||
BPath feedPath;
|
||||
|
||||
BObjectList<Feed> feeds;
|
||||
|
||||
while (subDir.GetNextEntry(&feedEntry) == B_OK
|
||||
&& feedEntry.GetPath(&feedPath) == B_OK)
|
||||
{
|
||||
Feed* feed = GetFeed(feedPath.Path());
|
||||
feeds.AddItem(feed);
|
||||
}
|
||||
|
||||
return feeds;
|
||||
}
|
||||
|
||||
|
||||
Feed*
|
||||
LocalSource::GetFeed(const char* identifier)
|
||||
{
|
||||
BFile file(identifier, B_READ_ONLY);
|
||||
time_t tt_lastDate = 0;
|
||||
BDateTime lastDate;
|
||||
BString title;
|
||||
BString url;
|
||||
BString hash;
|
||||
|
||||
file.ReadAttrString("Feed:name", &title);
|
||||
file.ReadAttrString("META:url", &url);
|
||||
file.ReadAttrString("Feed:hash", &hash);
|
||||
file.ReadAttr("Feed:when", B_TIME_TYPE, 0, &tt_lastDate, sizeof(time_t));
|
||||
lastDate.SetTime_t(tt_lastDate);
|
||||
|
||||
Feed* feed = new Feed(identifier, title.String(), url.String());
|
||||
feed->SetHash(hash);
|
||||
feed->SetLastDate(lastDate);
|
||||
return feed;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::Fetch(Feed* feed)
|
||||
{
|
||||
BFile cacheFile = BFile(feed->Identifier(), B_READ_WRITE | B_CREATE_FILE);
|
||||
BString hash;
|
||||
|
||||
int32 result = fetch(feed->Url(), &cacheFile, &hash, 30);
|
||||
feed->SetHash(hash);
|
||||
|
||||
if (result == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::Parse(Feed* feed)
|
||||
{
|
||||
if (IsUpdated(feed) == false)
|
||||
return true;
|
||||
|
||||
if (_IsAtom(feed) == true)
|
||||
return _AtomParse(feed);
|
||||
else if (_IsRss(feed) == true)
|
||||
return _RssParse(feed);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::AddFeed(Feed* newFeed)
|
||||
{
|
||||
if (newFeed->Identifier().IsEmpty() == true) {
|
||||
BPath subPath = _SubscriptionPath();
|
||||
subPath.Append(urlToFilename(newFeed->Url()));
|
||||
newFeed->SetIdentifier(subPath.Path());
|
||||
}
|
||||
EditFeed(newFeed);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::EditFeed(Feed* updated)
|
||||
{
|
||||
Feed* old = GetFeed(updated->Identifier());
|
||||
BFile file(updated->Identifier(), B_READ_WRITE | B_CREATE_FILE);
|
||||
|
||||
if (old->Title() != updated->Title()) {
|
||||
BString title = updated->Title();
|
||||
file.WriteAttrString("Feed:name", &title);
|
||||
}
|
||||
if (old->Url() != updated->Url()) {
|
||||
BString url = updated->Url().UrlString();
|
||||
file.WriteAttrString("META:url", &url);
|
||||
}
|
||||
if (old->Hash() != updated->Hash()) {
|
||||
BString hash = updated->Hash();
|
||||
file.WriteAttrString("Feed:hash", &hash);
|
||||
}
|
||||
if (old->Date() != updated->Date()) {
|
||||
time_t tt_date = updated->Date().Time_t();
|
||||
file.WriteAttr("Feed:when", B_TIME_TYPE, 0, &tt_date, sizeof(time_t));
|
||||
}
|
||||
|
||||
BString type("application/x-feed-source");
|
||||
file.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, type.String(),
|
||||
type.CountChars() + 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::RemoveFeed(Feed* mortonta)
|
||||
{
|
||||
BEntry entry(mortonta->Identifier().String());
|
||||
entry.Remove();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::IsUpdated(Feed* feed)
|
||||
{
|
||||
return (GetFeed(feed->Identifier())->Hash() != feed->Hash());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_IsAtom(Feed* feed)
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(feed->Identifier().String());
|
||||
|
||||
if (xml.FirstChildElement("feed"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_IsRss(Feed* feed)
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(feed->Identifier().String());
|
||||
|
||||
if (xml.FirstChildElement("rss"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_AtomParse(Feed* atomFeed)
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(atomFeed->Identifier().String());
|
||||
|
||||
tinyxml2::XMLElement* xfeed = xml.FirstChildElement("feed");
|
||||
|
||||
_AtomRootParse(atomFeed, xfeed);
|
||||
_AtomEntriesParse(atomFeed, xfeed);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::_AtomRootParse(Feed* feed, tinyxml2::XMLElement* xfeed)
|
||||
{
|
||||
tinyxml2::XMLElement* xentry = xfeed->FirstChildElement("entry");
|
||||
|
||||
bool set = false;
|
||||
|
||||
_SetTitle(feed, xfeed->FirstChildElement("title"));
|
||||
|
||||
set = _SetDate(feed, xfeed->FirstChildElement("updated"));
|
||||
if (!set)
|
||||
set = _SetDate(feed, xfeed->FirstChildElement("published"));
|
||||
if (!set && xentry)
|
||||
set = _SetDate(feed, xentry->FirstChildElement("updated"));
|
||||
if (!set && xentry)
|
||||
set = _SetDate(feed, xentry->FirstChildElement("published"));
|
||||
|
||||
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
||||
logString.ReplaceAll("%source%", feed->Title().String());
|
||||
logString.ReplaceAll("%url%", feed->Url().UrlString());
|
||||
|
||||
std::cout << logString.String();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::_AtomEntriesParse(Feed* feed, tinyxml2::XMLElement* xfeed)
|
||||
{
|
||||
tinyxml2::XMLElement* xentry;
|
||||
|
||||
xentry = xfeed->FirstChildElement("entry");
|
||||
|
||||
int entryCount = _XmlCountSiblings(xentry, "entry");
|
||||
BObjectList<Entry> entries(entryCount, true);
|
||||
|
||||
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
||||
BString entryStr;
|
||||
entryStr << entryCount;
|
||||
logString.ReplaceAll("%count%", entryStr);
|
||||
|
||||
std::cout << logString.String();
|
||||
|
||||
while (xentry) {
|
||||
Entry* newEntry = _AtomEntryParse(feed, xentry);
|
||||
if (newEntry != NULL)
|
||||
entries.AddItem(newEntry);
|
||||
xentry = xentry->NextSiblingElement("entry");
|
||||
}
|
||||
|
||||
feed->SetEntries(entries);
|
||||
}
|
||||
|
||||
|
||||
Entry*
|
||||
LocalSource::_AtomEntryParse(Feed* feed, tinyxml2::XMLElement* xentry)
|
||||
{
|
||||
Entry* newEntry = new Entry();
|
||||
|
||||
tinyxml2::XMLElement* xcontent = xentry->FirstChildElement("content");
|
||||
tinyxml2::XMLElement* xmedia = xentry->FirstChildElement("media:group");
|
||||
tinyxml2::XMLPrinter xprinter;
|
||||
|
||||
newEntry->SetTitle(xentry->FirstChildElement("title"));
|
||||
newEntry->SetPostUrl(xentry->FirstChildElement("link")->Attribute("href"));
|
||||
newEntry->SetFeedTitle(feed->Title());
|
||||
|
||||
bool set = false;
|
||||
set = newEntry->SetDescription(xentry->FirstChildElement("summary"));
|
||||
if (!set)
|
||||
set = newEntry->SetDescription(xentry->FirstChildElement("description"));
|
||||
if (!set && xmedia)
|
||||
set = newEntry->SetDescription(
|
||||
xmedia->FirstChildElement("media:description"));
|
||||
|
||||
set = _SetDate(newEntry, xentry->FirstChildElement("updated"));
|
||||
if (!set)
|
||||
set = _SetDate(newEntry, xentry->FirstChildElement("published"));
|
||||
|
||||
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
|
||||
feed->SetDate(newEntry->Date());
|
||||
|
||||
if (xcontent) {
|
||||
xcontent->Accept(&xprinter);
|
||||
newEntry->SetContent(xprinter.CStr());
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_RssParse(Feed* feed)
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
xml.LoadFile(feed->Identifier().String());
|
||||
|
||||
tinyxml2::XMLElement* xchan = xml.FirstChildElement("rss")->FirstChildElement("channel");
|
||||
|
||||
_RssRootParse(feed, xchan);
|
||||
_RssEntriesParse(feed, xchan);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::_RssRootParse(Feed* feed, tinyxml2::XMLElement* xchan)
|
||||
{
|
||||
_SetTitle(feed, xchan->FirstChildElement("title"));
|
||||
_SetDate(feed, xchan->FirstChildElement("lastBuildDate"));
|
||||
|
||||
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
||||
logString.ReplaceAll("%source%", feed->Title().String());
|
||||
logString.ReplaceAll("%url%", feed->Url().UrlString());
|
||||
|
||||
std::cout << logString.String();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LocalSource::_RssEntriesParse(Feed* feed, tinyxml2::XMLElement* xchan)
|
||||
{
|
||||
tinyxml2::XMLElement* xitem;
|
||||
|
||||
xitem = xchan->FirstChildElement("item");
|
||||
|
||||
int entryCount = _XmlCountSiblings(xitem, "item");
|
||||
BObjectList<Entry> entries = BObjectList<Entry>(entryCount, true);
|
||||
|
||||
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
||||
BString entryStr;
|
||||
entryStr << entryCount;
|
||||
logString.ReplaceAll("%count%", entryStr);
|
||||
|
||||
std::cout << logString.String();
|
||||
|
||||
while (xitem) {
|
||||
entries.AddItem(_RssEntryParse(feed, xitem));
|
||||
xitem = xitem->NextSiblingElement("item");
|
||||
}
|
||||
feed->SetEntries(entries);
|
||||
}
|
||||
|
||||
|
||||
Entry*
|
||||
LocalSource::_RssEntryParse(Feed* feed, tinyxml2::XMLElement* xitem)
|
||||
{
|
||||
Entry* newEntry = new Entry();
|
||||
|
||||
newEntry->SetTitle(xitem->FirstChildElement("title"));
|
||||
newEntry->SetDescription(xitem->FirstChildElement("description"));
|
||||
_SetDate(newEntry, xitem->FirstChildElement("pubDate"));
|
||||
newEntry->SetPostUrl(xitem->FirstChildElement("link"));
|
||||
newEntry->SetContent(xitem->FirstChildElement("content:encoded"));
|
||||
newEntry->SetFeedTitle(feed->Title());
|
||||
|
||||
if (feed->Date() == NULL || feed->Date() < newEntry->Date())
|
||||
feed->SetDate(newEntry->Date());
|
||||
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_SetTitle(Feed* feed, tinyxml2::XMLElement* elem)
|
||||
{
|
||||
if (elem != NULL && feed->Title().IsEmpty() == true)
|
||||
return feed->SetTitle(elem->GetText());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LocalSource::_SetDate(Feed* feed, const char* dateCStr)
|
||||
{
|
||||
if (dateCStr == NULL)
|
||||
return false;
|
||||
return feed->SetDate(feedDateToBDate(dateCStr));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_SetDate(Feed* feed, tinyxml2::XMLElement* elem)
|
||||
{
|
||||
if (elem == NULL)
|
||||
return false;
|
||||
return _SetDate(feed, elem->GetText());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_SetDate(Entry* entry, const char* dateStr)
|
||||
{
|
||||
if (dateStr == NULL)
|
||||
return false;
|
||||
return entry->SetDate(feedDateToBDate(dateStr));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LocalSource::_SetDate(Entry* entry, tinyxml2::XMLElement* elem)
|
||||
{
|
||||
if (elem == NULL)
|
||||
return false;
|
||||
|
||||
return _SetDate(entry, elem->GetText());
|
||||
}
|
||||
|
||||
|
||||
// Count the amount of siblings to an element of given type name
|
||||
int
|
||||
LocalSource::_XmlCountSiblings (tinyxml2::XMLElement* xsibling,
|
||||
const char* sibling_name)
|
||||
{
|
||||
int count = 0;
|
||||
while (xsibling) {
|
||||
count++;
|
||||
xsibling = xsibling->NextSiblingElement(sibling_name);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
BPath
|
||||
LocalSource::_SubscriptionPath()
|
||||
{
|
||||
BPath subPath;
|
||||
find_directory(B_USER_SETTINGS_DIRECTORY, &subPath);
|
||||
subPath.Append("Pogger");
|
||||
subPath.Append("Subscriptions");
|
||||
BDirectory subDir(subPath.Path());
|
||||
if (subDir.InitCheck() == B_ENTRY_NOT_FOUND) {
|
||||
subDir.CreateDirectory(subPath.Path(), &subDir);
|
||||
|
||||
subPath.Append("Haiku Project");
|
||||
Feed defaultSub(BUrl("https://www.haiku-os.org/blog/index.xml"),
|
||||
BEntry(subPath.Path()));
|
||||
defaultSub.SetTitle("Haiku Project");
|
||||
defaultSub.Filetize();
|
||||
}
|
||||
|
||||
BEntry feedEntry;
|
||||
BPath feedPath;
|
||||
BStringList feeds;
|
||||
|
||||
while (subDir.GetNextEntry(&feedEntry) == B_OK
|
||||
&& feedEntry.GetPath(&feedPath) == B_OK)
|
||||
feeds.Add(feedPath.Path());
|
||||
return feeds;
|
||||
return subPath;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,9 +9,44 @@
|
|||
|
||||
class LocalSource : public FeedSource {
|
||||
public:
|
||||
static BStringList Feeds();
|
||||
LocalSource();
|
||||
|
||||
const char* name = "Local";
|
||||
static BObjectList<Feed> Feeds();
|
||||
|
||||
static Feed* GetFeed(const char* identifier);
|
||||
|
||||
static bool Fetch(Feed* feed);
|
||||
static bool Parse(Feed* feed);
|
||||
|
||||
static void AddFeed(Feed* newFeed);
|
||||
static void EditFeed(Feed* updated);
|
||||
static void RemoveFeed(Feed* mortonta);
|
||||
|
||||
static bool IsUpdated(Feed* feed);
|
||||
|
||||
private:
|
||||
static bool _IsAtom(Feed* feed);
|
||||
static bool _IsRss(Feed* feed);
|
||||
|
||||
static bool _AtomParse(Feed* feed);
|
||||
static void _AtomRootParse(Feed* feed, tinyxml2::XMLElement*);
|
||||
static void _AtomEntriesParse(Feed* feed, tinyxml2::XMLElement*);
|
||||
static Entry* _AtomEntryParse(Feed* feed, tinyxml2::XMLElement*);
|
||||
|
||||
static bool _RssParse(Feed* feed);
|
||||
static void _RssRootParse(Feed* feed, tinyxml2::XMLElement*);
|
||||
static void _RssEntriesParse(Feed* feed, tinyxml2::XMLElement*);
|
||||
static Entry* _RssEntryParse(Feed* feed, tinyxml2::XMLElement*);
|
||||
|
||||
static bool _SetTitle(Feed* feed, tinyxml2::XMLElement*);
|
||||
|
||||
static bool _SetDate(Feed* feed, const char*);
|
||||
static bool _SetDate(Feed* feed, tinyxml2::XMLElement*);
|
||||
static bool _SetDate(Entry* entry, const char*);
|
||||
static bool _SetDate(Entry* entry, tinyxml2::XMLElement*);
|
||||
|
||||
static int _XmlCountSiblings(tinyxml2::XMLElement*, const char*);
|
||||
static BPath _SubscriptionPath();
|
||||
};
|
||||
|
||||
|
||||
|
|
111
src/RssFeed.cpp
111
src/RssFeed.cpp
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
#include "RssFeed.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <Catalog.h>
|
||||
|
||||
#include "App.h"
|
||||
#include "Entry.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
#define B_TRANSLATION_CONTEXT "RssFeed"
|
||||
|
||||
|
||||
RssFeed::RssFeed()
|
||||
{
|
||||
fTitle = BString("");
|
||||
fXmlUrl = BString("");
|
||||
}
|
||||
|
||||
|
||||
RssFeed::RssFeed(Feed* feed)
|
||||
: RssFeed::RssFeed()
|
||||
{
|
||||
SetTitle(feed->Title());
|
||||
SetXmlUrl(feed->XmlUrl());
|
||||
SetCachePath(feed->CachePath());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RssFeed::Parse()
|
||||
{
|
||||
tinyxml2::XMLDocument xml;
|
||||
fEntries = BObjectList<Entry>(5, true);
|
||||
|
||||
Feed::Parse();
|
||||
|
||||
xml.LoadFile(CachePath().String());
|
||||
tinyxml2::XMLElement* xchan = xml.FirstChildElement("rss")->FirstChildElement("channel");
|
||||
|
||||
RootParse(xchan);
|
||||
ParseEntries(xchan);
|
||||
|
||||
Filetize();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RssFeed::RootParse(tinyxml2::XMLElement* xchan)
|
||||
{
|
||||
_SetTitle(xchan->FirstChildElement("title"));
|
||||
_SetDate(xchan->FirstChildElement("lastBuildDate"));
|
||||
|
||||
BString logString(B_TRANSLATE("Channel '%source%' at %url%:\n"));
|
||||
logString.ReplaceAll("%source%", fTitle.String());
|
||||
logString.ReplaceAll("%url%", fXmlUrl.UrlString());
|
||||
|
||||
std::cout << logString.String();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RssFeed::EntryParse(tinyxml2::XMLElement* xitem)
|
||||
{
|
||||
Entry* newEntry = new Entry();
|
||||
|
||||
newEntry->SetTitle(xitem->FirstChildElement("title"));
|
||||
newEntry->SetDescription(xitem->FirstChildElement("description"));
|
||||
newEntry->SetDate(xitem->FirstChildElement("pubDate"));
|
||||
newEntry->SetPostUrl(xitem->FirstChildElement("link"));
|
||||
newEntry->SetContent(xitem->FirstChildElement("content:encoded"));
|
||||
newEntry->SetFeedTitle(fTitle);
|
||||
|
||||
if (fDate == NULL || fDate < newEntry->Date())
|
||||
fDate = newEntry->Date();
|
||||
|
||||
_AddEntry(newEntry);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RssFeed::ParseEntries(tinyxml2::XMLElement* xchan)
|
||||
{
|
||||
tinyxml2::XMLElement* xitem;
|
||||
|
||||
xitem = xchan->FirstChildElement("item");
|
||||
|
||||
int entryCount = _XmlCountSiblings(xitem, "item");
|
||||
fEntries = BObjectList<Entry>(entryCount, true);
|
||||
|
||||
BString logString(B_TRANSLATE("\t-%count% entries-\n"));
|
||||
BString entryStr;
|
||||
entryStr << entryCount;
|
||||
logString.ReplaceAll("%count%", entryStr);
|
||||
|
||||
std::cout << logString.String();
|
||||
|
||||
while (xitem) {
|
||||
EntryParse(xitem);
|
||||
xitem = xitem->NextSiblingElement("item");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef RSS_FEED_H
|
||||
#define RSS_FEED_H
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include "Feed.h"
|
||||
|
||||
|
||||
class RssFeed: public Feed {
|
||||
public:
|
||||
RssFeed();
|
||||
RssFeed(Feed*);
|
||||
|
||||
void Parse();
|
||||
void RootParse(tinyxml2::XMLElement*);
|
||||
void EntryParse(tinyxml2::XMLElement*);
|
||||
void ParseEntries(tinyxml2::XMLElement*);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
Ŝarĝante…
Reference in New Issue