user-agent, expired login, TODOs
This commit is contained in:
parent
b06233daa8
commit
dc598223a6
@ -10,7 +10,7 @@
|
||||
# .apk file extension == .tar.gz file extension
|
||||
|
||||
pkgname=py3-nextcast
|
||||
pkgver=0.0.3
|
||||
pkgver=0.0.4
|
||||
pkgrel=1
|
||||
pkgdesc="Nextcloud Podcast Client"
|
||||
url="https://git.privacy1st.de/langfingaz/nextcast"
|
||||
|
11
README.md
11
README.md
@ -55,7 +55,7 @@ cp Alpine/APKGBUILD ~/.local/var/pmbootstrap/cache_git/pmaports/main/py3-nextcas
|
||||
pmbootstrap apkbuild_parse py3-nextcast
|
||||
pmbootstrap checksum py3-nextcast
|
||||
pmbootstrap build --arch aarch64 py3-nextcast
|
||||
#=> build x86_64/py3-nextcast-0.0.3-r1.apk
|
||||
#=> build x86_64/py3-nextcast-0.0.4-r1.apk
|
||||
```
|
||||
|
||||
```shell
|
||||
@ -63,8 +63,8 @@ pmbootstrap shutdown
|
||||
```
|
||||
|
||||
```shell
|
||||
ls ~/.local/var/pmbootstrap/packages/edge/x86_64/py3-nextcast-0.0.3-r1.apk
|
||||
ls ~/.local/var/pmbootstrap/packages/edge/x86_64/py3-nextcast-pyc-0.0.3-r1.apk
|
||||
ls ~/.local/var/pmbootstrap/packages/edge/x86_64/py3-nextcast-0.0.4-r1.apk
|
||||
ls ~/.local/var/pmbootstrap/packages/edge/x86_64/py3-nextcast-pyc-0.0.4-r1.apk
|
||||
```
|
||||
|
||||
Sideload to your postmarketOS phone:
|
||||
@ -74,3 +74,8 @@ Sideload to your postmarketOS phone:
|
||||
```shell
|
||||
pmbootstrap sideload --host yodaEnchilada --user yoda --arch aarch64 --install-key py3-nextcast
|
||||
```
|
||||
|
||||
## TODOs
|
||||
|
||||
- [ ] If action refers unknown episode/podcast, create it.
|
||||
- [ ] Executable to update local list of podcasts and episodes.
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
[metadata]
|
||||
name = nextcast
|
||||
version = 0.0.3
|
||||
version = 0.0.4
|
||||
author = Daniel Langbein
|
||||
author_email = daniel@systemli.org
|
||||
description = Nextcloud Podcast Client
|
||||
|
@ -7,6 +7,7 @@ from typing import Callable
|
||||
from nextcast import mpv
|
||||
from nextcast.cli_gui import list_selection, CancelButton, TopButton
|
||||
from nextcast.log import Log
|
||||
from nextcast.nextcloud import LoginExpired
|
||||
from nextcast.podcast import User, Episode, Podcast
|
||||
from nextcast.user_manager import UserManager
|
||||
|
||||
@ -36,6 +37,10 @@ def podcast_loop(prev: Callable, user: User) -> None:
|
||||
title='Select podcast',
|
||||
top_buttons=[back_str],
|
||||
)
|
||||
except LoginExpired as _e:
|
||||
Log.error(f'Nextcloud login of user {user.user_id} expired. Please re-login.')
|
||||
UserManager.logout(user)
|
||||
exit()
|
||||
except CancelButton as _e:
|
||||
exit()
|
||||
except TopButton as e:
|
||||
|
@ -3,6 +3,7 @@
|
||||
from nextcast import mpv
|
||||
from nextcast.cli_gui import list_selection, CancelButton
|
||||
from nextcast.log import Log
|
||||
from nextcast.nextcloud import LoginExpired
|
||||
from nextcast.user_manager import UserManager
|
||||
|
||||
|
||||
@ -12,7 +13,12 @@ def main():
|
||||
Log.config(pause_on_error=True)
|
||||
|
||||
user = UserManager.from_interaction()
|
||||
episodes = user.get_episodes()
|
||||
try:
|
||||
episodes = user.get_episodes()
|
||||
except LoginExpired as _e:
|
||||
Log.error(f'Nextcloud login of user {user.user_id} expired. Please re-login.')
|
||||
UserManager.logout(user)
|
||||
exit()
|
||||
|
||||
# Paused episodes.
|
||||
pauseds = [episode for episode in episodes
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import socket
|
||||
import time
|
||||
import webbrowser
|
||||
from datetime import datetime, timedelta
|
||||
@ -110,6 +111,10 @@ class EpisodeAction:
|
||||
return json.dumps(self.to_json())
|
||||
|
||||
|
||||
class LoginExpired(Exception):
|
||||
status_code = 401
|
||||
|
||||
|
||||
class NextcloudApi:
|
||||
@classmethod
|
||||
def login(cls, domain: str) -> tuple[str, str, str]:
|
||||
@ -117,7 +122,11 @@ class NextcloudApi:
|
||||
Login flow documentation:
|
||||
https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html
|
||||
"""
|
||||
r = requests.post(f'https://{domain}/index.php/login/v2')
|
||||
# Set human-readable user agent when logging in.
|
||||
# This string is displayed in the Nextcloud web interface under "Device & Sessions".
|
||||
hostname: str = socket.gethostname()
|
||||
headers = {'User-Agent': f'nextcast@{hostname}'}
|
||||
r = requests.post(f'https://{domain}/index.php/login/v2', headers=headers)
|
||||
assert r.status_code == 200
|
||||
content = json.loads(r.text)
|
||||
webbrowser.open(content['login'], new=0, autoraise=True)
|
||||
@ -149,12 +158,16 @@ class NextcloudApi:
|
||||
https://github.com/thrillfall/nextcloud-gpodder/blob/main/README.md#subscription
|
||||
|
||||
:return: The URLs of the subscribed podcast feeds. Example: 'https://logbuch-netzpolitik.de/feed/opus'
|
||||
:raises LoginExpired:
|
||||
"""
|
||||
url = f'{server}/apps/gpoddersync/subscriptions'
|
||||
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
|
||||
if r.status_code == LoginExpired.status_code:
|
||||
raise LoginExpired()
|
||||
else:
|
||||
assert r.status_code == 200, r.status_code
|
||||
content = json.loads(r.text)
|
||||
return content['add']
|
||||
|
||||
@ -188,6 +201,8 @@ class NextcloudApi:
|
||||
-> tuple[datetime, list[EpisodeAction]]:
|
||||
"""
|
||||
https://github.com/thrillfall/nextcloud-gpodder/blob/main/README.md#episode-action
|
||||
|
||||
:raises LoginExpired:
|
||||
"""
|
||||
if types is None:
|
||||
types = [ea_type for ea_type in EpisodeActionType]
|
||||
@ -196,7 +211,10 @@ class NextcloudApi:
|
||||
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
|
||||
if r.status_code == LoginExpired.status_code:
|
||||
raise LoginExpired()
|
||||
else:
|
||||
assert r.status_code == 200, r.status_code
|
||||
content = json.loads(r.text)
|
||||
|
||||
timestamp = datetime_util.from_timestamp(content['timestamp'])
|
||||
@ -208,6 +226,9 @@ class NextcloudApi:
|
||||
@classmethod
|
||||
def push_episode_actions(cls, server: str, login_name: str, app_password: str,
|
||||
actions: list[EpisodeAction]) -> None:
|
||||
"""
|
||||
:raises LoginExpired:
|
||||
"""
|
||||
data = [action.to_json() for action in actions]
|
||||
|
||||
url = f'{server}/apps/gpoddersync/episode_action/create'
|
||||
@ -216,7 +237,10 @@ class NextcloudApi:
|
||||
headers = {'Content-type': 'application/json'}
|
||||
r = requests.post(url, auth=(login_name, app_password),
|
||||
headers=headers, data=data_str)
|
||||
assert r.status_code == 200
|
||||
if r.status_code == LoginExpired.status_code:
|
||||
raise LoginExpired()
|
||||
else:
|
||||
assert r.status_code == 200, r.status_code
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -35,6 +35,11 @@ class UserManager:
|
||||
cls._get_content()[user.user_id] = user.app_password
|
||||
json_util.write_credentials(cls._get_content())
|
||||
|
||||
@classmethod
|
||||
def remove_from_disk(cls, user: User) -> None:
|
||||
cls._get_content().pop(user.user_id, None)
|
||||
json_util.write_credentials(cls._get_content())
|
||||
|
||||
@classmethod
|
||||
def from_interaction(cls) -> User:
|
||||
"""
|
||||
@ -84,3 +89,7 @@ class UserManager:
|
||||
cls.add_to_disk(user)
|
||||
|
||||
return user
|
||||
|
||||
@classmethod
|
||||
def logout(cls, user: User):
|
||||
cls.remove_from_disk(user)
|
||||
|
Loading…
Reference in New Issue
Block a user