diff --git a/lazy_player/__init__.py b/lazy_player/__init__.py
index e4503f5..4f5b6cc 100644
--- a/lazy_player/__init__.py
+++ b/lazy_player/__init__.py
@@ -130,15 +130,18 @@ class MainWindow(Gtk.ApplicationWindow):
                     self._navigate_to(file_item.full_path)
                     return
 
-                position = file_item.load_attribute("position", 0)
-                duration = file_item.load_attribute("duration", 1) or 1
+                position = file_item.saved_position
+                duration = file_item.saved_duration
 
                 if (position / duration) >= 0.99:
                     position = 0
 
                 # Start playing the video
-                track = file_item.load_attribute("subtitle_track", -2)
-                self.video_player.play(file_item.full_path, position, track)
+                self.video_player.play(
+                    file_item.full_path,
+                    position,
+                    file_item.saved_subtitle_track,
+                )
                 self.last_position_save = self.now
 
                 self.stack.set_visible_child_name("player")
@@ -204,8 +207,8 @@ class MainWindow(Gtk.ApplicationWindow):
                 icon.set_from_icon_name("folder-symbolic")
                 icon.set_css_classes(["file-icon"])
             else:
-                position = item.load_attribute("position", 0)
-                duration = item.load_attribute("duration", 1) or 1
+                position = item.saved_position
+                duration = item.saved_duration
 
                 if position == 0:
                     icon.set_from_icon_name("media-playback-start-symbolic")
@@ -218,7 +221,7 @@ class MainWindow(Gtk.ApplicationWindow):
                     icon.set_css_classes(["file-icon", "in-progress"])
 
         label.set_text(item.name)
-        item.connect("notify::attrs-changed", update_icon)
+        item.connect("notify::saved-position", update_icon)
         update_icon()
 
     def _navigate_to(self, path: Path):
@@ -336,15 +339,15 @@ class MainWindow(Gtk.ApplicationWindow):
         if file_item.file_type == FileType.DIRECTORY:
             return
 
-        position = file_item.load_attribute("position", 0)
-        duration = file_item.load_attribute("duration", 1) or 1
+        position = file_item.saved_position
+        duration = file_item.saved_duration
 
         # If position exists and is >= 99% through, clear it
         if position > 0 and (position / duration) >= 0.99:
-            file_item.save_attribute("position", None)
+            file_item.saved_position = 0
         else:
             # Otherwise mark as complete
-            file_item.save_attribute("position", duration)
+            file_item.saved_position = duration
 
         # Force the list to update the changed item
         self.list_model.items_changed(self.selection_model.get_selected(), 1, 1)
@@ -395,11 +398,11 @@ class MainWindow(Gtk.ApplicationWindow):
 
         position = self.video_player.get_position()
         if position is not None:
-            file_item.save_attribute("position", position)
+            file_item.saved_position = position
 
         duration = self.video_player.get_duration()
         if duration is not None:
-            file_item.save_attribute("duration", duration)
+            file_item.saved_duration = duration
 
     def show_overlay_text(self, text: str, timeout_seconds: float = 1.0) -> None:
         """Show text in a centered overlay that disappears after timeout"""
diff --git a/lazy_player/file_model.py b/lazy_player/file_model.py
index 8e53c56..3bba64a 100644
--- a/lazy_player/file_model.py
+++ b/lazy_player/file_model.py
@@ -22,29 +22,53 @@ class FileItem(GObject.Object):
 
     __gtype_name__ = "FileItem"
 
-    attrs_changed = GObject.Property(type=int, default=0)
-
     def __init__(self, name: str, file_type: FileType, full_path: Path):
         super().__init__()
-        self.attrs_changed = 0
         self.name = name
         self.file_type = file_type
         self.full_path = full_path
 
-    @overload
-    def load_attribute(self, name: str, dfl: str) -> str: ...
+    @GObject.Property(type=GObject.TYPE_UINT64)
+    def saved_position(self) -> int:
+        return self._load_attribute("position", 0)
+
+    @saved_position.setter
+    def set_saved_position(self, value: int):
+        self._save_attribute("position", value if value else None)
+        self.notify("saved-position")
+
+    @GObject.Property(type=GObject.TYPE_UINT64)
+    def saved_duration(self):
+        return self._load_attribute("duration", 1) or 1
+
+    @saved_duration.setter
+    def set_saved_duration(self, value: int):
+        self._save_attribute("duration", value if value > 0 else None)
+        self.notify("saved-duration")
+
+    @GObject.Property(type=int)
+    def saved_subtitle_track(self):
+        return self._load_attribute("subtitle_track", -2)
+
+    @saved_subtitle_track.setter
+    def set_saved_subtitle_track(self, value: int):
+        self._save_attribute("subtitle_track", value if value >= -1 else None)
+        self.notify("saved-subtitle-track")
 
     @overload
-    def load_attribute(self, name: str, dfl: int) -> int: ...
+    def _load_attribute(self, name: str, dfl: str) -> str: ...
 
-    def load_attribute(self, name: str, dfl: str | int) -> str | int:
+    @overload
+    def _load_attribute(self, name: str, dfl: int) -> int: ...
+
+    def _load_attribute(self, name: str, dfl: str | int) -> str | int:
         try:
             strval = os.getxattr(self.full_path, f"user.lazy_player.{name}")
             return type(dfl)(strval)
         except OSError:
             return dfl
 
-    def save_attribute(self, name: str, value: str | float | int | None) -> None:
+    def _save_attribute(self, name: str, value: str | float | int | None) -> None:
         try:
             if value is None:
                 os.removexattr(self.full_path, f"user.lazy_player.{name}")
@@ -53,8 +77,6 @@ class FileItem(GObject.Object):
         except OSError:
             pass
 
-        self.notify("attrs-changed")
-
 
 class FileListModel(GObject.Object, Gio.ListModel):
     """A ListModel implementation for FileItems"""