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 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 ### Updating with the server

View file

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

View file

@ -45,28 +45,35 @@ class FeatherApp:
dirpath.rmdir() dirpath.rmdir()
removed_directories.add(dirpath) removed_directories.add(dirpath)
def mark_deleted_as_read(self): def toggle_read_flag_for_deleted(self):
"""Mark articles 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/unread on the server"""
config = self.config config = self.config
client_session = self.get_client_session() client_session = self.get_client_session()
if config.update_lock.exists(): 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 return
marked_as_read = 0 marked_as_read, marked_as_unread = 0, 0
to_mark_as_read = [] to_mark_as_read = []
to_mark_as_unread = []
for json_path in config.json_root.glob("*.json"): for json_path in config.json_root.glob("*.json"):
article = FileArticle(config, json_path) article = FileArticle(config, json_path)
html_path = config.html_root / article.html_path html_path = config.html_root / article.html_path
if not html_path.exists(): if not html_path.exists():
to_mark_as_read.append(article.id) if article.unread:
marked_as_read += 1 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): 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): def synchronize_with_server(self):
"""Synchronize articles from the server, generating and deleting JSON and HTML files accordingly""" """Synchronize articles from the server, generating and deleting JSON and HTML files accordingly"""
@ -81,7 +88,7 @@ class FeatherApp:
categories = client_session.list_categories() categories = client_session.list_categories()
for category in categories: for category in categories:
print(f" Updating category {category.title}") print(f" Synchronizing category {category.title}")
remaining, continuation = True, 0 remaining, continuation = True, 0
while remaining: while remaining:
@ -116,14 +123,14 @@ class FeatherApp:
def synchronize(self): def synchronize(self):
"""Do a full feather update""" """Do a full feather update"""
self.mark_deleted_as_read() self.toggle_read_flag_for_deleted()
self.synchronize_with_server() self.synchronize_with_server()
if self.config.hide_empty_categories: if self.config.hide_empty_categories:
self.remove_empty_categories() self.remove_empty_categories()
def synchronize_local_changes(self): def synchronize_local_changes(self):
"""Upload local changes (read articles) to the server""" """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: if self.config.hide_empty_categories:
self.remove_empty_categories() self.remove_empty_categories()