timestamp and unknown episodes
This commit is contained in:
parent
7a9e4babeb
commit
73be70bd4c
@ -3,6 +3,13 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
def test():
|
||||
utc_timestamp = 1699546201
|
||||
dt = from_timestamp(utc_timestamp)
|
||||
assert dt == datetime(2023, 11, 9, 16, 10, 1, tzinfo=timezone.utc)
|
||||
assert to_timestamp(dt) == utc_timestamp
|
||||
|
||||
|
||||
def now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
@ -16,7 +23,7 @@ def now_timestamp() -> int:
|
||||
|
||||
|
||||
def to_timestamp(dt: datetime) -> int:
|
||||
return round(datetime.timestamp(dt)) * 1000
|
||||
return round(datetime.timestamp(dt))
|
||||
|
||||
|
||||
def from_timestamp(timestamp: int) -> datetime:
|
||||
@ -34,3 +41,7 @@ def from_str(dt_str: str) -> datetime:
|
||||
|
||||
def fmt() -> str:
|
||||
return '%Y-%m-%dT%H:%M:%S'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
@ -56,6 +56,13 @@ def cache(file_url: str, cache_dir: Path) -> Path:
|
||||
"""
|
||||
file = hashed_location(file_url, cache_dir)
|
||||
|
||||
# Replace plain HTTP with HTTPS
|
||||
http = 'http://'
|
||||
https = 'https://'
|
||||
if file_url.startswith(http):
|
||||
file_url = https + file_url[len(http):]
|
||||
assert file_url.startswith(https), file_url
|
||||
|
||||
# If the file is missing locally, it is downloaded.
|
||||
if not file.is_file():
|
||||
Log.info(f'Downloading {file_url} ...')
|
||||
|
@ -106,6 +106,9 @@ class EpisodeAction:
|
||||
def position(self, value: int):
|
||||
self._position = value
|
||||
|
||||
def __str__(self):
|
||||
return json.dumps(self.to_json())
|
||||
|
||||
|
||||
class NextcloudApi:
|
||||
@classmethod
|
||||
@ -156,22 +159,21 @@ class NextcloudApi:
|
||||
return content['add']
|
||||
|
||||
@classmethod
|
||||
def fetch_play_actions(cls, server: str, login_name: str, app_password: str, user_id: str):
|
||||
def fetch_play_actions(cls, server: str, login_name: str, app_password: str, user_id: str) -> list[EpisodeAction]:
|
||||
"""
|
||||
:return: Return new episode actions since last function call where EpisodeActionType is PLAY.
|
||||
"""
|
||||
timestamp_dict = read_timestamp()
|
||||
timestamp = timestamp_dict.get(user_id, 0)
|
||||
timestamp = datetime_util.from_str(timestamp_dict.get(user_id, '1970-01-01T00:00:00'))
|
||||
|
||||
episode_actions = NextcloudApi.fetch_episode_actions(
|
||||
new_timestamp, episode_actions = NextcloudApi.fetch_episode_actions(
|
||||
server=server,
|
||||
login_name=login_name,
|
||||
app_password=app_password,
|
||||
types=[EpisodeActionType.PLAY],
|
||||
since=timestamp)
|
||||
|
||||
timestamp = datetime_util.now_timestamp()
|
||||
timestamp_dict[user_id] = timestamp
|
||||
timestamp_dict[user_id] = datetime_util.to_str(new_timestamp)
|
||||
write_timestamp(timestamp_dict)
|
||||
|
||||
return episode_actions
|
||||
@ -182,24 +184,25 @@ class NextcloudApi:
|
||||
login_name: str,
|
||||
app_password: str,
|
||||
types: list[EpisodeActionType] = None,
|
||||
since: int = 0) -> list[EpisodeAction]:
|
||||
since: datetime = datetime_util.from_timestamp(0)) \
|
||||
-> tuple[datetime, list[EpisodeAction]]:
|
||||
"""
|
||||
https://github.com/thrillfall/nextcloud-gpodder/blob/main/README.md#episode-action
|
||||
"""
|
||||
if types is None:
|
||||
types = [ea_type for ea_type in EpisodeActionType]
|
||||
|
||||
url = f'{server}/apps/gpoddersync/episode_action?since={since}'
|
||||
url = f'{server}/apps/gpoddersync/episode_action?since={datetime_util.to_timestamp(since)}'
|
||||
Log.info(f'Downloading {url} ...')
|
||||
# auth parameter: https://requests.readthedocs.io/en/latest/user/authentication/#basic-authentication
|
||||
r = requests.get(url, auth=(login_name, app_password))
|
||||
assert r.status_code == 200
|
||||
content = json.loads(r.text)
|
||||
|
||||
_timestamp = datetime_util.from_timestamp(content['timestamp'])
|
||||
timestamp = datetime_util.from_timestamp(content['timestamp'])
|
||||
|
||||
episode_actions = [EpisodeAction.from_json(action) for action in content['actions']]
|
||||
return [episode_action for episode_action in episode_actions
|
||||
return timestamp, [episode_action for episode_action in episode_actions
|
||||
if episode_action.action in types]
|
||||
|
||||
@classmethod
|
||||
|
@ -73,13 +73,25 @@ class User:
|
||||
episodes_by_id: dict[str, Episode] = {e.episode_id: e
|
||||
for p in self._podcasts
|
||||
for e in p.episodes}
|
||||
|
||||
# fix broken "guid" of podcast "Forklart" which includes heading and tailing newlines
|
||||
for episode_action in episode_actions:
|
||||
episode_action.guid = episode_action.guid.strip()
|
||||
# fix local files on Android phone
|
||||
header = 'antennapod_local:content://com.android'
|
||||
episode_actions = [ea for ea in episode_actions if not ea.podcast.startswith(header)]
|
||||
|
||||
for episode_action in episode_actions:
|
||||
if episode_action.episode_id in episodes_by_id:
|
||||
episode = episodes_by_id[episode_action.episode_id]
|
||||
print(f'\t{episode.title} {episode.get_position()} -> {episode_action.position}')
|
||||
activity = PlaybackActivity(self.user_id, episode_action.position, episode_action.timestamp)
|
||||
episode.set_activity(activity)
|
||||
episode.set_duration(episode_action.total)
|
||||
episode.write()
|
||||
else:
|
||||
# TODO
|
||||
Log.error(f'Action refers locally unknown episode: {episode_action}')
|
||||
except requests.exceptions.ConnectionError as _e:
|
||||
Log.error(f'Could not fetch new actions.')
|
||||
|
||||
@ -266,6 +278,9 @@ class Episode:
|
||||
def set_activity(self, new_activity: PlaybackActivity):
|
||||
prev_activity = self.get_activity()
|
||||
|
||||
if new_activity.timestamp == prev_activity.timestamp and new_activity.position == prev_activity.position:
|
||||
# This is the current activity, nothing has changed.
|
||||
return
|
||||
if new_activity.timestamp <= prev_activity.timestamp:
|
||||
raise ValueError(f'Expected timestamp of new activity to be greater.'
|
||||
f' Previous: {prev_activity.timestamp}.'
|
||||
|
Loading…
Reference in New Issue
Block a user