Skip to content

Commit

Permalink
Merge branch 'master' into new-book-overview
Browse files Browse the repository at this point in the history
  • Loading branch information
rdbende committed Jul 14, 2024
2 parents 9aafa2a + e233056 commit 18f2c27
Show file tree
Hide file tree
Showing 20 changed files with 488 additions and 488 deletions.
3 changes: 1 addition & 2 deletions cozy/app_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
from cozy.control.filesystem_monitor import FilesystemMonitor
from cozy.control.offline_cache import OfflineCache
from cozy.media.files import Files
from cozy.media.gst_player import GstPlayer
from cozy.media.player import Player
from cozy.media.player import GstPlayer, Player
from cozy.model.book import Book
from cozy.model.database_importer import DatabaseImporter
from cozy.model.library import Library
Expand Down
47 changes: 26 additions & 21 deletions cozy/control/mpris.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
from cozy.application_settings import ApplicationSettings
from cozy.control.artwork_cache import ArtworkCache
from cozy.ext import inject
from cozy.media.player import NS_TO_SEC, US_TO_SEC, Player
from cozy.media.player import Player
from cozy.model.book import Book
from cozy.report import reporter

log = logging.getLogger("mpris")

CamelCasePattern = re.compile(r"(?<!^)(?=[A-Z])")

NS_TO_US = 1e3


def to_snake_case(name: str) -> str:
return CamelCasePattern.sub("_", name).lower()
Expand Down Expand Up @@ -69,9 +71,7 @@ def __init__(self, connection: Gio.DBusConnection, path: str) -> None:

for interface in Gio.DBusNodeInfo.new_for_xml(self.__doc__).interfaces:
for method in interface.methods:
self.method_inargs[method.name] = tuple(
arg.signature for arg in method.in_args
)
self.method_inargs[method.name] = tuple(arg.signature for arg in method.in_args)
out_sig = [arg.signature for arg in method.out_args]
self.method_outargs[method.name] = "(" + "".join(out_sig) + ")"

Expand Down Expand Up @@ -111,10 +111,7 @@ def on_method_call(
except Exception as e:
log.error(e)
reporter.exception("mpris", e)
reporter.error(
"mpris",
f"MPRIS method call failed with method name: {method_name}",
)
reporter.error("mpris", f"MPRIS method call failed with method name: {method_name}")
invocation.return_dbus_error(
f"{interface_name}.Error.Failed", "Internal exception occurred"
)
Expand All @@ -126,7 +123,7 @@ def on_method_call(
result = (result,)

out_args = self.method_outargs[method_name]
if out_args != "()" and result[0]:
if out_args != "()" and result[0] is not None:
variant = GLib.Variant(out_args, result)
invocation.return_value(variant)
else:
Expand Down Expand Up @@ -257,31 +254,25 @@ def stop(self):
self._player.destroy()

def set_position(self, track_id: str, position: int):
self._player.position = position / US_TO_SEC
self._player.position = position * NS_TO_US

def seek(self, offset: int):
self._player.position = self._player.position / NS_TO_SEC + offset / US_TO_SEC
self._player.position = self._player.position + offset * NS_TO_US

def get(self, interface: str, property_name: str) -> GLib.Variant:
if property_name in {"CanQuit", "CanControl"}:
return GLib.Variant("b", True)
elif property_name in {"CanRaise", "HasTrackList"}:
return GLib.Variant("b", False)
elif property_name in {
"CanGoNext",
"CanGoPrevious",
"CanPlay",
"CanPause",
"CanSeek",
}:
elif property_name in {"CanGoNext", "CanGoPrevious", "CanPlay", "CanPause", "CanSeek"}:
return GLib.Variant("b", self._player.loaded_book is not None)
elif property_name in {"SupportedUriSchemes", "SupportedMimeTypes"}:
return GLib.Variant("as", [])

# Might raise an AttributeError. We handle that in Server.on_method_call
return getattr(self, to_snake_case(property_name))

def get_all(self, interface):
def get_all(self, interface) -> dict[str, GLib.Variant]:
if interface == self.MEDIA_PLAYER2_INTERFACE:
properties = (
"CanQuit",
Expand All @@ -305,9 +296,15 @@ def get_all(self, interface):
"CanControl",
"Volume",
)
else:
return {}

return {property: self.get(interface, property) for property in properties}

def set(self, interface: str, property_name: str, value) -> None:
# Might raise an AttributeError. We handle that in Server.on_method_call
return setattr(self, to_snake_case(property_name), value)

def properties_changed(self, iface_name, changed_props, invalidated_props):
self._bus.emit_signal(
None,
Expand Down Expand Up @@ -350,6 +347,10 @@ def position(self):
def volume(self):
return GLib.Variant("d", self._player.volume)

@volume.setter
def volume(self, new_value: float) -> None:
self._player.volume = new_value

def _get_track_id(self) -> float:
"""
Track IDs must be unique even up to the point that if a song
Expand All @@ -370,14 +371,18 @@ def _get_new_metadata(self, book: Book | None = None) -> dict[str, GLib.Variant]
title=book.current_chapter.name,
album=book.name,
artist=[book.author],
length=book.current_chapter.length * US_TO_SEC,
length=book.current_chapter.length / NS_TO_US,
url=uri_template.format(path=book.current_chapter.file),
artwork_uri=self._artwork_cache.get_album_art_path(book, 256),
)
return metadata.to_dict()

def _on_player_changed(self, event: str, _) -> None:
if event == "chapter-changed":
if event == "position":
self.properties_changed(
self.MEDIA_PLAYER2_PLAYER_INTERFACE, {"Position": self.position}, []
)
elif event == "chapter-changed":
self._on_current_changed()
elif event == "play":
self._on_status_changed("Playing")
Expand Down
28 changes: 0 additions & 28 deletions cozy/control/string_representation.py

This file was deleted.

102 changes: 102 additions & 0 deletions cozy/control/time_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from datetime import datetime
from gettext import ngettext

from gi.repository import Gst


def ns_to_time(
nanoseconds: int, max_length: int | None = None, include_seconds: bool = True
) -> str:
"""
Converts nanoseconds to a string with the following appearance:
hh:mm:ss
:param nanoseconds: int
"""
m, s = divmod(nanoseconds / Gst.SECOND, 60)
h, m = divmod(m, 60)

if max_length:
max_m, _ = divmod(max_length, 60)
max_h, max_m = divmod(max_m, 60)
else:
max_h = h
max_m = m

if max_h >= 10:
result = "%02d:%02d" % (h, m)
elif max_h >= 1:
result = "%d:%02d" % (h, m)
else:
result = "%02d" % m

if include_seconds:
result += ":%02d" % s

return result


def ns_to_human_readable(nanoseconds: int) -> str:
"""
Create a string with the following format:
6 hours 1 minute
45 minutes
21 seconds
:param seconds: int
"""
m, s = divmod(nanoseconds / Gst.SECOND, 60)
h, m = divmod(m, 60)
h = int(h)
m = int(m)
s = int(s)

result = ""
if h > 0 and m > 0:
result = (
ngettext("{hours} hour", "{hours} hours", h).format(hours=h)
+ " "
+ ngettext("{minutes} minute", "{minutes} minutes", m).format(minutes=m)
)
elif h > 0:
result = ngettext("{hours} hour", "{hours} hours", h).format(hours=h)
elif m > 0:
result = ngettext("{minutes} minute", "{minutes} minutes", m).format(minutes=m)
elif s > 0:
result = ngettext("{seconds} second", "{seconds} seconds", s).format(seconds=s)
else:
result = _("finished")

return result


def date_delta_to_human_readable(unix_time):
"""
Converts the date to the following strings (from today):
today
yesterday
x days ago
x week(s) ago
x month(s) ago
x year(s) ago
"""
date = datetime.fromtimestamp(unix_time)
past = datetime.today().date() - date.date()
days = int(past.days)
weeks = int(days / 7)
months = int(days / 30)
years = int(months / 12)

if unix_time < 1:
return _("never")
elif days < 1:
return _("today")
elif days < 2:
return _("yesterday")
elif days < 7:
return _("{days} days ago").format(days=days)
elif weeks < 5:
return ngettext("{weeks} week ago", "{weeks} weeks ago", weeks).format(weeks=weeks)
elif months < 12:
return ngettext("{months} month ago", "{months} months ago", months).format(months=months)
else:
return ngettext("{years} year ago", "{years} years ago", years).format(years=years)
Loading

0 comments on commit 18f2c27

Please sign in to comment.