Conversation
The timestamp is being stored in the DB as an integer, but in a field of type varchar(255). So the DB cannot be used to sort of filter, because it would sort the integers alphabetically. The workaround is to load all of the results into memory and then throw away any that don't meet the optional `since=` filter. fixes oxtyped#39
|
I couldn't figure out how to run the tests. They seem to require a pre-existing database, but I'm not sure what's expected there, and I didn't see anything in the repo that would generate it. I did manually test this with antennapod, but I don't know if the automated tests would pass after these changes. |
|
If anyone else wants to try this in the meantime, you can use container image Just take note that I made a new `Containerfile' which has the process running as non-root (user 1001), so be mindful of permissions when mounting storage there. Update: I forgot to mention that I also put the binary in |
|
so when I would like to try it I have to change the image to |
|
Here's what I did to run it locally on Fedora and use a directory on my system to store the data: If you're still using docker, the same arguments should work. And you might not need the |
|
When I use the following docker compose file in Portainer and start with an empty gpodder2go-folder I got the following error message: services: No database found, intializing gpodder2go ... |
|
Yeah, that error message is misleading, and it's a bug in the sqlite3 driver. The problem is just that the process can't write to the The process running in the container will run as UID 1001, instead of running as root. That's a good thing. We just need to make sure that user 1001 can write to the data directory. Sorry, I haven't used docker in many years, so I can't test with it. One of the main reasons to use podman is that it makes it easier to run things as non-root. Can you run |
|
Thanks for the explanation. I fixed it via giving read+write access to everyone in the Synoloy DSM GUI for this folder. Then I tried to add a user via: But I got this error: |
|
Ah yes, sorry, I forgot to mention one other change! I put the binary in the standard location for binaries instead of at /. Just remove the |
|
Yes, without |
|
After synchronisation I recognised that the sync is failed in AntennaPod. I have attached my podcast subscriptions and also the log from portainer. |
|
I suggest opening a new issue on this repo for that, as it's likely unrelated to the episode API. The auth check happens early in the code flow, before any of the API handlers get invoked. Reading the code, I don't see how the |
|
Now I started with a fresh installation of AntennaPod and added my subscriptions and checked it with a second device. |
|
This PR is pretty interesting! |
|
@mhrivnak thanks for this, the doc change you made was the game-changer for me (manual group setup). I hope to have time to play with your fix(es), as the sole reason I want a sync server is episode progress. Thanks for working on this and sharing 🙏 I just posted a PR to your fork with a minor doc cleanup, so your new docs are readable in a browser https://github.com/clach04/gpodder2go/tree/patch-1#antennapod |
|
I am working on a complete rewrite of gpodder called rpodder https://github.com/thekoma/rpodder . It's heavily vibe coded, nonetheless does what I need and some fancy thing more. @mhrivnak PR are welcome. |
|
Thanks for the heads up @thekoma , that looks neat! It's about triple the size of this and has more features than I need right now, so I'll see how my usage goes. @mhrivnak I put together a python script for step 5 in your notes about setting up a group - its a bit of a hammer as it marks all devices for sync #!/usr/bin/env python
# -*- coding: us-ascii -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
#
"""Mark ALL devices known to username, as syncronized.
gpodder2go notes
1. Need fixed version with Episode sync support - https://github.com/oxtyped/gpodder2go/pull/44
2. can't use a device with no subscriptions, see readme in https://github.com/clach04/gpodder2go for specific steps. This script handles step #5
"""
import argparse
import json
import os
import sys
import requests # pip install requests
def doit(args, timeout=10):
"""arguments:
args.username
args.password
args.url
"""
url_root = args.url
while url_root.endswith('/'):
print(url_root)
url_root = url_root[:-1]
print(url_root)
# https://gpoddernet.readthedocs.io/en/latest/api/reference/auth.html
url_login = url_root + '/api/2/auth/%s/login.json' % (args.username,)
# https://gpoddernet.readthedocs.io/en/latest/api/reference/devices.html
# https://gpoddernet.readthedocs.io/en/latest/api/reference/sync.html
url_devices = url_root + '/api/2/devices/%s.json' % (args.username,)
url_sync_devices = url_root + '/api/2/sync-devices/%s.json' % (args.username,)
# Using a session object allows for (in-memory) cookie persistence
session = requests.Session()
try:
# use HTTP Basic Auth and login
auth = (args.username, args.password) if args.username and args.password else None
response = session.post(url_login, auth=auth, timeout=timeout)
response.raise_for_status()
print(f"--- POST Status Code: {response.status_code} ---")
print(response.text)
all_devices_ids = []
response = session.get(url_devices, timeout=timeout)
response.raise_for_status()
print(f"--- GET Status Code: {response.status_code} ---")
print(response.text)
print(response.json())
print(json.dumps(response.json(), indent=4))
print(dir(response))
for device in response.json():
all_devices_ids.append(device["id"])
response = session.get(url_sync_devices, timeout=timeout)
response.raise_for_status()
print(f"--- GET Status Code: {response.status_code} ---")
print(response.text)
print(all_devices_ids)
sync_dict = {
"synchronize": [
all_devices_ids # Yes, list in a list (array in an array)
],
#"stop-synchronize": [],
#"stop-synchronize": None,
}
response = session.post(url_sync_devices, auth=auth, timeout=timeout, json=sync_dict)
response.raise_for_status()
print(f"--- POST Status Code: {response.status_code} ---")
print(response.text)
response = session.get(url_sync_devices, timeout=timeout)
response.raise_for_status()
print(f"--- GET Status Code: {response.status_code} ---")
print(response.text)
except requests.exceptions.HTTPError as errh:
print(f"HTTP Error: {errh}")
except requests.exceptions.ConnectionError as errc:
print(f"Error Connecting: {errc}")
except requests.exceptions.Timeout as errt:
print(f"Timeout Error: {errt}")
except requests.exceptions.RequestException as err:
print(f"An unexpected error occurred: {err}")
finally:
# Close the session to free up resources
session.close()
def main(argv=None):
if argv is None:
argv = sys.argv
# FIXME argv
parser = argparse.ArgumentParser(description="Root URL for gpodder compatible server, omit /api/2/...")
parser.add_argument("url", help="The target URL (e.g., http://localhost:3005)")
parser.add_argument("-u", "--username", help="Username for authentication")
parser.add_argument("-p", "--password", help="Password for authentication")
args = parser.parse_args()
print('Python %s on %s' % (sys.version.replace('\n', ' '), sys.platform.replace('\n', ' ')))
doit(args)
return 0
if __name__ == "__main__":
sys.exit(main()) |
I've tested this with Antennapod, and episode sync is working perfectly.
This merges #18 and completes that work. Thanks @TheBlusky !
fixes #39