mirror of
https://codeberg.org/Reuh/feather.git
synced 2025-10-27 10:09:32 +00:00
refactor: rename item -> article
This commit is contained in:
parent
07e9d208b1
commit
a587340865
6 changed files with 48 additions and 47 deletions
10
README.md
10
README.md
|
|
@ -8,19 +8,19 @@ start with pictures/gif each time
|
|||
|
||||
Directories, sorting/searching by date/title (a.k.a. using a file manager)
|
||||
|
||||
### Reading an item
|
||||
### Reading an article
|
||||
|
||||
opening an item
|
||||
opening an article
|
||||
|
||||
### Marking items as read
|
||||
### Marking articles as read
|
||||
|
||||
Delete
|
||||
|
||||
See read items in the trash can
|
||||
See read articles in the trash can
|
||||
|
||||
### Updating with the server
|
||||
|
||||
Call `feather sync` to synchronize all local data with the server (read items, new items from the server, etc.).
|
||||
Call `feather sync` to synchronize all local data with the server (read articles, new articles from the server, etc.).
|
||||
|
||||
`feather daemon`
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ def main():
|
|||
description="file-based RSS reader client"
|
||||
)
|
||||
parser.add_argument(
|
||||
"action", choices=("sync", "sync-up", "sync-down", "daemon", "regenerate", "clear-data"),
|
||||
help="sync: perform a full synchronization with the server; sync-up: only synchronize local changes to the server (e.g. items read locally); sync-down: only synchronize remote change from the server (e.g. new items or items read from another device); daemon: start in daemon mode (will keep performing synchronizations periodically until process is stopped); regenerate: regenerate all HTML files from the local data; clear-data: remove all local data"
|
||||
"action",
|
||||
choices=("sync", "sync-up", "sync-down", "daemon", "regenerate", "clear-data"),
|
||||
help="sync: perform a full synchronization with the server; sync-up: only synchronize local changes to the server (e.g. articles read locally); sync-down: only synchronize remote change from the server (e.g. new articles or articles read from another device); daemon: start in daemon mode (will keep performing synchronizations periodically until process is stopped); regenerate: regenerate all HTML files from the local data; clear-data: remove all local data",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class ClientSession(ABC):
|
|||
config: Config
|
||||
|
||||
@abstractmethod
|
||||
def mark_as_read(self, item_ids: list[ArticleId]):
|
||||
def mark_as_read(self, articles_ids: list[ArticleId]):
|
||||
"""Mark all the given articles as read."""
|
||||
pass
|
||||
|
||||
|
|
@ -41,8 +41,8 @@ class GReaderSession(ClientSession):
|
|||
self.auth_token = self.greader.login(config.server_user, config.server_password)
|
||||
self.csrf_token = self.greader.get_token(self.auth_token)
|
||||
|
||||
def mark_as_read(self, item_ids: list[ArticleId]):
|
||||
self.greader.edit_tags(self.auth_token, self.csrf_token, item_ids=item_ids, add_tags=[google_reader.STREAM_READ])
|
||||
def mark_as_read(self, articles_ids: list[ArticleId]):
|
||||
self.greader.edit_tags(self.auth_token, self.csrf_token, item_ids=articles_ids, add_tags=[google_reader.STREAM_READ])
|
||||
|
||||
def list_categories(self) -> list[Category]:
|
||||
categories = [tag for tag in self.greader.list_tags(self.auth_token) if tag.type == "folder"]
|
||||
|
|
@ -95,8 +95,8 @@ class TTRSession(ClientSession):
|
|||
self.ttrss.login()
|
||||
self.feeds = {}
|
||||
|
||||
def mark_as_read(self, item_ids: list[ArticleId]):
|
||||
self.ttrss.mark_read(item_ids)
|
||||
def mark_as_read(self, articles_ids: list[ArticleId]):
|
||||
self.ttrss.mark_read(articles_ids)
|
||||
|
||||
def list_categories(self) -> list[Category]:
|
||||
self.feeds = {}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ user = "username"
|
|||
# (Required) Password/API password used to connect to the server
|
||||
# Can be set through the environment variable SERVER_PASSWORD.
|
||||
password = "password"
|
||||
# How many items to retrieve at most from the server in a single request. Lower values will make synchronization slower, higher values might make the server complain.
|
||||
# How many articles to retrieve at most from the server in a single request. Lower values will make synchronization slower, higher values might make the server complain.
|
||||
# If you are missing articles after a sync, it might be because this value is too high.
|
||||
# If you are using the Google Reader API: servers should be okay with up to 1000.
|
||||
# If you are using the ttrss API: servers should be okay with up to 200.
|
||||
|
|
@ -123,8 +123,8 @@ timezone = "Etc/UTC"
|
|||
format = "%Y-%m-%d %H:%M"
|
||||
|
||||
[daemon]
|
||||
# When running in daemon mode, feather will download changes from the server (new items, items read state) every <sync_down_every> seconds.
|
||||
# When running in daemon mode, feather will download changes from the server (new articles, articles read state) every <sync_down_every> seconds.
|
||||
sync_down_every = 900
|
||||
# When running in daemon mode, feather will upload local changes to the server (read items) every <sync_up_every> seconds.
|
||||
# When running in daemon mode, feather will upload local changes to the server (read articles) every <sync_up_every> seconds.
|
||||
sync_up_every = 60
|
||||
|
||||
|
|
|
|||
|
|
@ -112,12 +112,12 @@ class Article(ABC):
|
|||
|
||||
def write_json(self):
|
||||
stored_fields = ("id", "unread", "title", "published", "published_formatted", "updated", "updated_formatted", "author", "summary", "content", "feed_title", "feed_url", "feed_icon_url", "feed_order", "article_url", "comments_url", "language", "image_url", "html_path")
|
||||
item_json = { field: getattr(self, field) for field in stored_fields }
|
||||
item_json["category"] = self.category.asdict()
|
||||
article_json = { field: getattr(self, field) for field in stored_fields }
|
||||
article_json["category"] = self.category.asdict()
|
||||
if self.json_path.exists():
|
||||
raise Exception
|
||||
with self.json_path.open("w") as f:
|
||||
json.dump(item_json, f)
|
||||
json.dump(article_json, f)
|
||||
def delete_json(self):
|
||||
self.json_path.unlink()
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ class FileArticle(Article):
|
|||
self.config = config
|
||||
self.json_path = json_path
|
||||
|
||||
item_json = json.load(json_path.open("r"))
|
||||
for field in item_json:
|
||||
setattr(self, field, item_json[field])
|
||||
self.category = Category.fromdict(item_json["category"])
|
||||
article_json = json.load(json_path.open("r"))
|
||||
for field in article_json:
|
||||
setattr(self, field, article_json[field])
|
||||
self.category = Category.fromdict(article_json["category"])
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ class FeatherApp:
|
|||
removed_directories.add(dirpath)
|
||||
|
||||
def mark_deleted_as_read(self):
|
||||
"""Mark items that are in the JSON directory but with missing HTML file as read on the server"""
|
||||
"""Mark articles that are in the JSON directory but with missing HTML file as read on the server"""
|
||||
config = self.config
|
||||
client_session = self.get_client_session()
|
||||
|
||||
if config.update_lock.exists():
|
||||
print("The previous synchronization was aborted, not marking any item as read in order to avoid collateral damage")
|
||||
print("The previous synchronization was aborted, not marking any article as read in order to avoid collateral damage")
|
||||
return
|
||||
|
||||
marked_as_read = 0
|
||||
|
|
@ -67,18 +67,18 @@ class FeatherApp:
|
|||
for i in range(0, len(to_mark_as_read), config.articles_per_query):
|
||||
client_session.mark_as_read(to_mark_as_read[i:i+config.articles_per_query])
|
||||
|
||||
print(f"Marked {marked_as_read} items as read")
|
||||
print(f"Marked {marked_as_read} articles as read")
|
||||
|
||||
def synchronize_with_server(self):
|
||||
"""Synchronize items from the server, generating and deleting JSON and HTML files accordingly"""
|
||||
"""Synchronize articles from the server, generating and deleting JSON and HTML files accordingly"""
|
||||
config = self.config
|
||||
client_session = self.get_client_session()
|
||||
|
||||
config.update_lock.touch()
|
||||
print("Synchronizing with server...")
|
||||
|
||||
new_items, updated_items = 0, 0
|
||||
grabbed_item_paths = set()
|
||||
new_articles, updated_articles = 0, 0
|
||||
grabbed_article_paths = set()
|
||||
|
||||
categories = client_session.list_categories()
|
||||
for category in categories:
|
||||
|
|
@ -92,27 +92,27 @@ class FeatherApp:
|
|||
else:
|
||||
remaining = False
|
||||
|
||||
for item in articles:
|
||||
json_path = item.json_path
|
||||
grabbed_item_paths.add(json_path)
|
||||
for article in articles:
|
||||
json_path = article.json_path
|
||||
grabbed_article_paths.add(json_path)
|
||||
if not json_path.exists():
|
||||
item.write()
|
||||
new_items += 1
|
||||
article.write()
|
||||
new_articles += 1
|
||||
else:
|
||||
old_item = FileArticle(config, json_path)
|
||||
if item.updated > old_item.updated:
|
||||
old_item.delete()
|
||||
item.write()
|
||||
updated_items += 1
|
||||
old_article = FileArticle(config, json_path)
|
||||
if article.updated > old_article.updated:
|
||||
old_article.delete()
|
||||
article.write()
|
||||
updated_articles += 1
|
||||
|
||||
# Remove items that we didn't get from the server but are in the JSON directory
|
||||
removed_items = 0
|
||||
for item_path in config.json_root.glob("*.json"):
|
||||
if not item_path in grabbed_item_paths:
|
||||
FileArticle(config, item_path).delete()
|
||||
removed_items += 1
|
||||
# Remove articles that we didn't get from the server but are in the JSON directory
|
||||
removed_articles = 0
|
||||
for article_path in config.json_root.glob("*.json"):
|
||||
if article_path not in grabbed_article_paths:
|
||||
FileArticle(config, article_path).delete()
|
||||
removed_articles += 1
|
||||
|
||||
print(f"Synchronization successful ({new_items} new items, {updated_items} updated, {removed_items} removed)")
|
||||
print(f"Synchronization successful ({new_articles} new articles, {updated_articles} updated, {removed_articles} removed)")
|
||||
config.update_lock.unlink()
|
||||
|
||||
def synchronize(self):
|
||||
|
|
@ -122,12 +122,12 @@ class FeatherApp:
|
|||
self.remove_empty_html_directories()
|
||||
|
||||
def synchronize_local_changes(self):
|
||||
"""Upload local changes (read items) to the server"""
|
||||
"""Upload local changes (read articles) to the server"""
|
||||
self.mark_deleted_as_read()
|
||||
self.remove_empty_html_directories()
|
||||
|
||||
def synchronize_remote_changes(self):
|
||||
"""Download remote changes (new items, items read from another device) from the server"""
|
||||
"""Download remote changes (new articles, articles read from another device) from the server"""
|
||||
self.synchronize_with_server()
|
||||
self.remove_empty_html_directories()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue