1
0
Fork 0
mirror of https://codeberg.org/Reuh/feather.git synced 2025-10-27 10:09:32 +00:00

feat: can mark articles as unread by deleting files

This commit is contained in:
Étienne Fildadut 2025-10-11 15:28:34 +02:00
parent 70a26e418f
commit 00001ed4b0
3 changed files with 39 additions and 19 deletions

View file

@ -16,7 +16,14 @@ opening an article
Delete
See read articles in the trash can
#### Handling read articles
See read articles in the trash can.
Or ask feather to keep read articles:
`server.only_sync_unread_articles = false`
`html.filename_template = "{% if unread %}☐{% else %}☑{% endif %} [{{ feed_title }}]\t{{ title }} ({{ published_formatted }}).html"`
### Updating with the server

View file

@ -14,8 +14,8 @@ class ClientSession(ABC):
config: Config
@abstractmethod
def mark_as_read(self, articles_ids: list[ArticleId]):
"""Mark all the given articles as read."""
def set_read_flag(self, article_ids: list[ArticleId], read: bool = True):
"""Mark all the given articles as read or unread."""
pass
@abstractmethod
@ -40,9 +40,12 @@ class GReaderSession(ClientSession):
self.greader = google_reader.Client(config.server_url)
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, 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 set_read_flag(self, article_ids: list[ArticleId], read: bool = True):
if read:
self.greader.edit_tags(self.auth_token, self.csrf_token, item_ids=article_ids, add_tags=[google_reader.STREAM_READ])
else:
self.greader.edit_tags(self.auth_token, self.csrf_token, item_ids=article_ids, remove_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"]
@ -101,8 +104,11 @@ class TTRSession(ClientSession):
self.ttrss.login()
self.feeds = {}
def mark_as_read(self, articles_ids: list[ArticleId]):
self.ttrss.mark_read(articles_ids)
def set_unread(self, article_ids: list[ArticleId], read: bool = True):
if read:
self.ttrss.mark_read(article_ids)
else:
self.ttrss.mark_unread(article_ids)
def list_categories(self) -> list[Category]:
self.feeds = {}

View file

@ -45,28 +45,35 @@ class FeatherApp:
dirpath.rmdir()
removed_directories.add(dirpath)
def mark_deleted_as_read(self):
"""Mark articles that are in the JSON directory but with missing HTML file as read on the server"""
def toggle_read_flag_for_deleted(self):
"""Mark articles that are in the JSON directory but with missing HTML file as read/unread 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 article as read in order to avoid collateral damage")
print("The previous synchronization was aborted, not marking any article as read/unread in order to avoid collateral damage")
return
marked_as_read = 0
marked_as_read, marked_as_unread = 0, 0
to_mark_as_read = []
to_mark_as_unread = []
for json_path in config.json_root.glob("*.json"):
article = FileArticle(config, json_path)
html_path = config.html_root / article.html_path
if not html_path.exists():
to_mark_as_read.append(article.id)
marked_as_read += 1
if article.unread:
to_mark_as_read.append(article.id)
marked_as_read += 1
else:
to_mark_as_unread.append(article.id)
marked_as_unread += 1
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])
client_session.set_read_flag(to_mark_as_read[i:i+config.articles_per_query], True)
for i in range(0, len(to_mark_as_unread), config.articles_per_query):
client_session.set_read_flag(to_mark_as_unread[i : i + config.articles_per_query], False)
print(f"Marked {marked_as_read} articles as read")
print(f"Marked {marked_as_read} articles as read, {marked_as_unread} unread")
def synchronize_with_server(self):
"""Synchronize articles from the server, generating and deleting JSON and HTML files accordingly"""
@ -81,7 +88,7 @@ class FeatherApp:
categories = client_session.list_categories()
for category in categories:
print(f" Updating category {category.title}")
print(f" Synchronizing category {category.title}")
remaining, continuation = True, 0
while remaining:
@ -116,14 +123,14 @@ class FeatherApp:
def synchronize(self):
"""Do a full feather update"""
self.mark_deleted_as_read()
self.toggle_read_flag_for_deleted()
self.synchronize_with_server()
if self.config.hide_empty_categories:
self.remove_empty_categories()
def synchronize_local_changes(self):
"""Upload local changes (read articles) to the server"""
self.mark_deleted_as_read()
self.toggle_read_flag_for_deleted()
if self.config.hide_empty_categories:
self.remove_empty_categories()