sebsync 1.0a2__tar.gz → 1.0a4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sebsync-1.0a4/PKG-INFO +85 -0
- sebsync-1.0a4/README.md +62 -0
- {sebsync-1.0a2 → sebsync-1.0a4}/pyproject.toml +1 -1
- {sebsync-1.0a2 → sebsync-1.0a4}/sebsync.py +71 -25
- sebsync-1.0a2/PKG-INFO +0 -54
- sebsync-1.0a2/README.md +0 -31
- {sebsync-1.0a2 → sebsync-1.0a4}/AUTHORS.md +0 -0
- {sebsync-1.0a2 → sebsync-1.0a4}/LICENSE +0 -0
sebsync-1.0a4/PKG-INFO
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: sebsync
|
|
3
|
+
Version: 1.0a4
|
|
4
|
+
Summary: Synchronize Standard Ebooks catalog with local ebooks.
|
|
5
|
+
Home-page: https://github.com/pbryan/sebsync/
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ebook,downloader,standard,book
|
|
8
|
+
Author: sebsync authors
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Other/Nonlisted Topic
|
|
18
|
+
Requires-Dist: click (>=8.1,<9.0)
|
|
19
|
+
Requires-Dist: requests (>=2.32,<3.0)
|
|
20
|
+
Project-URL: Documentation, https://github.com/pbryan/sebsync/tree/main/docs
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# sebsync
|
|
24
|
+
|
|
25
|
+
sebsync is a simple script to synchronize the [Standard Ebooks](https://standardebooks.org/)
|
|
26
|
+
catalog with your local EPUB collection.
|
|
27
|
+
|
|
28
|
+
## How it works
|
|
29
|
+
|
|
30
|
+
The script reads the Standard Ebooks [OPDS feed](https://standardebooks.org/feeds) and
|
|
31
|
+
reconciles the ebooks in their catalog against the EPUBs in your local filesystem,
|
|
32
|
+
downloading new ones or updating existing ones as necessary.
|
|
33
|
+
|
|
34
|
+
If a new ebook is detected in the catalog, then it will be downloaded into your downloads
|
|
35
|
+
directory. If an updated version of a book is detected in the catalog that is already in your
|
|
36
|
+
local filesystem, then the updated version will be downloaded and stored in the existing local
|
|
37
|
+
file.
|
|
38
|
+
|
|
39
|
+
The script can also detect extraneous local ebooks (local ebooks not found in the Standard
|
|
40
|
+
Ebooks catalog). This can can occur when Standard Ebooks changes the identifier of a previously
|
|
41
|
+
published ebook. It's a rare occurance, and it's generally safe to delete such files.
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
This script uses Standard Ebooks' OPDS feed to access metadata on all books their catalog. In
|
|
46
|
+
order to do so, you need to be a member of the Standard Ebooks
|
|
47
|
+
[Patrons Circle](https://standardebooks.org/donate#patrons-circle) (or have previously produced
|
|
48
|
+
an ebook for Standard Ebooks).
|
|
49
|
+
|
|
50
|
+
If you're not already a Standard Ebooks patron, please consider becoming one. It's a
|
|
51
|
+
tremendously valuable project, deserving of your support.
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
pipx install sebsync
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Example usage
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
sebsync --email addr@example.com --books /home/user/MyBooks --downloads /home/user/MyBooks/Downloads
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Questions and answers
|
|
66
|
+
|
|
67
|
+
Q1. *Why use a separate downloads directory for new ebooks?*
|
|
68
|
+
|
|
69
|
+
A1. This is a feature for those who want to easily recognize new ebooks, and to manually
|
|
70
|
+
rename and/or categorize them within their library. If this feature is not useful for you,
|
|
71
|
+
simply set `--downloads` to be the same directory as `--books`.
|
|
72
|
+
|
|
73
|
+
Q2. *What is pipx?*
|
|
74
|
+
|
|
75
|
+
A2. [pipx](https://pipx.pypa.io/) is a utility that allows Python packages like sebsync to
|
|
76
|
+
be installed in an isolated environment. This makes it really easy to install and run such
|
|
77
|
+
scripts without interfering with other Python package installations.
|
|
78
|
+
|
|
79
|
+
Q3. *Does this script support Kindle books?*
|
|
80
|
+
|
|
81
|
+
A3. Not presently. A cursory analysis of the Standard Ebooks AZW3 structure suggests that there
|
|
82
|
+
currently isn't a reliable method to reconcile Kindle ebooks with the OPDS feed catalog. If
|
|
83
|
+
this feature is important to you, please let us know by voting for
|
|
84
|
+
[this issue](https://github.com/pbryan/sebsync/issues/2) in GitHub.
|
|
85
|
+
|
sebsync-1.0a4/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# sebsync
|
|
2
|
+
|
|
3
|
+
sebsync is a simple script to synchronize the [Standard Ebooks](https://standardebooks.org/)
|
|
4
|
+
catalog with your local EPUB collection.
|
|
5
|
+
|
|
6
|
+
## How it works
|
|
7
|
+
|
|
8
|
+
The script reads the Standard Ebooks [OPDS feed](https://standardebooks.org/feeds) and
|
|
9
|
+
reconciles the ebooks in their catalog against the EPUBs in your local filesystem,
|
|
10
|
+
downloading new ones or updating existing ones as necessary.
|
|
11
|
+
|
|
12
|
+
If a new ebook is detected in the catalog, then it will be downloaded into your downloads
|
|
13
|
+
directory. If an updated version of a book is detected in the catalog that is already in your
|
|
14
|
+
local filesystem, then the updated version will be downloaded and stored in the existing local
|
|
15
|
+
file.
|
|
16
|
+
|
|
17
|
+
The script can also detect extraneous local ebooks (local ebooks not found in the Standard
|
|
18
|
+
Ebooks catalog). This can can occur when Standard Ebooks changes the identifier of a previously
|
|
19
|
+
published ebook. It's a rare occurance, and it's generally safe to delete such files.
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
This script uses Standard Ebooks' OPDS feed to access metadata on all books their catalog. In
|
|
24
|
+
order to do so, you need to be a member of the Standard Ebooks
|
|
25
|
+
[Patrons Circle](https://standardebooks.org/donate#patrons-circle) (or have previously produced
|
|
26
|
+
an ebook for Standard Ebooks).
|
|
27
|
+
|
|
28
|
+
If you're not already a Standard Ebooks patron, please consider becoming one. It's a
|
|
29
|
+
tremendously valuable project, deserving of your support.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pipx install sebsync
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Example usage
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
sebsync --email addr@example.com --books /home/user/MyBooks --downloads /home/user/MyBooks/Downloads
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Questions and answers
|
|
44
|
+
|
|
45
|
+
Q1. *Why use a separate downloads directory for new ebooks?*
|
|
46
|
+
|
|
47
|
+
A1. This is a feature for those who want to easily recognize new ebooks, and to manually
|
|
48
|
+
rename and/or categorize them within their library. If this feature is not useful for you,
|
|
49
|
+
simply set `--downloads` to be the same directory as `--books`.
|
|
50
|
+
|
|
51
|
+
Q2. *What is pipx?*
|
|
52
|
+
|
|
53
|
+
A2. [pipx](https://pipx.pypa.io/) is a utility that allows Python packages like sebsync to
|
|
54
|
+
be installed in an isolated environment. This makes it really easy to install and run such
|
|
55
|
+
scripts without interfering with other Python package installations.
|
|
56
|
+
|
|
57
|
+
Q3. *Does this script support Kindle books?*
|
|
58
|
+
|
|
59
|
+
A3. Not presently. A cursory analysis of the Standard Ebooks AZW3 structure suggests that there
|
|
60
|
+
currently isn't a reliable method to reconcile Kindle ebooks with the OPDS feed catalog. If
|
|
61
|
+
this feature is important to you, please let us know by voting for
|
|
62
|
+
[this issue](https://github.com/pbryan/sebsync/issues/2) in GitHub.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Synchronize Standard Ebooks catalog with local
|
|
1
|
+
"""Synchronize Standard Ebooks catalog with a local EPUB collection."""
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
import requests
|
|
@@ -6,7 +6,7 @@ import xml.etree.ElementTree as ElementTree
|
|
|
6
6
|
import zipfile
|
|
7
7
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
-
from datetime import datetime
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from requests.auth import HTTPBasicAuth
|
|
12
12
|
|
|
@@ -16,8 +16,22 @@ _quiet: bool = False
|
|
|
16
16
|
_verbose: bool = False
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
class Status:
|
|
20
|
+
NEW = click.style("N", fg="green")
|
|
21
|
+
UPDATE = click.style("U", fg="blue")
|
|
22
|
+
EXTRA = click.style("X", fg="yellow")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_epilog = f"""
|
|
26
|
+
The following statuses are reported for ebooks:
|
|
27
|
+
|
|
28
|
+
\b
|
|
29
|
+
{Status.NEW}: new (ebook found in Standard Ebooks catalog but not found locally)
|
|
30
|
+
{Status.UPDATE}: update (new version of ebook found in Standard Ebooks catalog)
|
|
31
|
+
{Status.EXTRA}: extraneous (local ebook was not found in Standard Ebooks catalog)
|
|
32
|
+
|
|
33
|
+
An extraneous ebook can occur when Standard Ebooks changes the identifier of a previously
|
|
34
|
+
published ebook. It's a rare occurance, and it's generally safe to delete such files.
|
|
21
35
|
"""
|
|
22
36
|
|
|
23
37
|
|
|
@@ -42,15 +56,37 @@ class LocalEbook:
|
|
|
42
56
|
modified: datetime
|
|
43
57
|
|
|
44
58
|
|
|
59
|
+
def _echo_status(path: Path, status: str) -> None:
|
|
60
|
+
if not _quiet:
|
|
61
|
+
click.echo(f"{status} {path}")
|
|
62
|
+
|
|
63
|
+
|
|
45
64
|
def _if_exists(path: Path) -> Path | None:
|
|
46
65
|
return path if path.exists() else None
|
|
47
66
|
|
|
48
67
|
|
|
49
|
-
def
|
|
68
|
+
def _fromisoformat(text: str) -> datetime:
|
|
69
|
+
"""Convert RFC 3339 string into datetime; compatible with Python 3.10."""
|
|
70
|
+
if not text.endswith("Z"):
|
|
71
|
+
raise ValueError("expecting RFC 3339 formatted string")
|
|
72
|
+
d = datetime.fromisoformat(text.rstrip("Z"))
|
|
73
|
+
return datetime(
|
|
74
|
+
d.year, d.month, d.day, d.hour, d.minute, d.second, d.microsecond, timezone.utc
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
type_selector = {
|
|
79
|
+
"compatible": "Recommended compatible epub",
|
|
80
|
+
"kobo": "Kobo Kepub epub",
|
|
81
|
+
"advanced": "Advanced epub",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_remote_ebooks(opds_url: str, email: str, type: str) -> dict[str, StandardEbook]:
|
|
50
86
|
"""Return Standard Ebooks metadata for EPUBs from the OPDS catalog."""
|
|
51
87
|
ns = {"atom": "http://www.w3.org/2005/Atom", "dc": "http://purl.org/dc/terms/"}
|
|
52
88
|
ebooks = {}
|
|
53
|
-
response = requests.get(
|
|
89
|
+
response = requests.get(opds_url, stream=True, auth=HTTPBasicAuth(email, ""))
|
|
54
90
|
response.raw.decode_content = True
|
|
55
91
|
root = ElementTree.parse(response.raw).getroot()
|
|
56
92
|
for entry in root.iterfind(".//atom:entry", ns):
|
|
@@ -58,10 +94,8 @@ def get_standard_ebooks(opds: str, email: str) -> dict[str, StandardEbook]:
|
|
|
58
94
|
id=entry.find("dc:identifier", ns).text,
|
|
59
95
|
title=entry.find("atom:title", ns).text,
|
|
60
96
|
author=entry.find("atom:author", ns).find("atom:name", ns).text,
|
|
61
|
-
href=entry.find(".//atom:link[@title='
|
|
62
|
-
|
|
63
|
-
],
|
|
64
|
-
updated=datetime.fromisoformat(entry.find("atom:updated", ns).text),
|
|
97
|
+
href=entry.find(f".//atom:link[@title='{type_selector[type]}']", ns).attrib["href"],
|
|
98
|
+
updated=_fromisoformat(entry.find("atom:updated", ns).text),
|
|
65
99
|
)
|
|
66
100
|
ebooks[ebook.id] = ebook
|
|
67
101
|
return ebooks
|
|
@@ -91,7 +125,7 @@ def get_local_ebooks(dir: Path) -> dict[str, LocalEbook]:
|
|
|
91
125
|
id=id.text,
|
|
92
126
|
title=metadata.find(".//dc:title", ns).text,
|
|
93
127
|
path=path,
|
|
94
|
-
modified=
|
|
128
|
+
modified=_fromisoformat(modified.text),
|
|
95
129
|
)
|
|
96
130
|
ebooks[ebook.id] = ebook
|
|
97
131
|
return ebooks
|
|
@@ -99,8 +133,6 @@ def get_local_ebooks(dir: Path) -> dict[str, LocalEbook]:
|
|
|
99
133
|
|
|
100
134
|
def download_ebook(url: str, path: Path) -> None:
|
|
101
135
|
"""Download the ebook at the specified URL into the specified path."""
|
|
102
|
-
if not _quiet:
|
|
103
|
-
click.echo(f"Download: {path}")
|
|
104
136
|
if not _dry_run:
|
|
105
137
|
response = requests.get(url, stream=True)
|
|
106
138
|
with path.open("wb") as file:
|
|
@@ -126,12 +158,12 @@ def ebook_filename(ebook: StandardEbook) -> str:
|
|
|
126
158
|
@click.option(
|
|
127
159
|
"--books",
|
|
128
160
|
help="directory where local books are stored",
|
|
129
|
-
type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
|
|
161
|
+
type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True, path_type=Path),
|
|
130
162
|
default=_if_exists(Path.home() / "Books"),
|
|
131
163
|
)
|
|
132
164
|
@click.option(
|
|
133
165
|
"--downloads",
|
|
134
|
-
help="directory where ebooks are downloaded",
|
|
166
|
+
help="directory where new ebooks are downloaded",
|
|
135
167
|
type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True, path_type=Path),
|
|
136
168
|
default=_if_exists(Path.home() / "Downloads"),
|
|
137
169
|
)
|
|
@@ -142,7 +174,7 @@ def ebook_filename(ebook: StandardEbook) -> str:
|
|
|
142
174
|
)
|
|
143
175
|
@click.option(
|
|
144
176
|
"--email",
|
|
145
|
-
help="email to authenticate with Standard Ebooks",
|
|
177
|
+
help="email address to authenticate with Standard Ebooks",
|
|
146
178
|
required=True,
|
|
147
179
|
)
|
|
148
180
|
@click.option(
|
|
@@ -155,13 +187,26 @@ def ebook_filename(ebook: StandardEbook) -> str:
|
|
|
155
187
|
help="suppress non-error messages",
|
|
156
188
|
is_flag=True,
|
|
157
189
|
)
|
|
190
|
+
@click.option(
|
|
191
|
+
"--type",
|
|
192
|
+
type=click.Choice(["compatible", "kobo", "advanced"]),
|
|
193
|
+
help="EPUB type to download",
|
|
194
|
+
default="compatible",
|
|
195
|
+
)
|
|
158
196
|
@click.option(
|
|
159
197
|
"--verbose",
|
|
160
198
|
help="increase verbosity",
|
|
161
199
|
is_flag=True,
|
|
162
200
|
)
|
|
163
201
|
def sebsync(
|
|
164
|
-
books: str,
|
|
202
|
+
books: str,
|
|
203
|
+
downloads: str,
|
|
204
|
+
dry_run: bool,
|
|
205
|
+
email: str,
|
|
206
|
+
opds: str,
|
|
207
|
+
quiet: bool,
|
|
208
|
+
type: str,
|
|
209
|
+
verbose: bool,
|
|
165
210
|
):
|
|
166
211
|
global _dry_run
|
|
167
212
|
global _verbose
|
|
@@ -171,28 +216,29 @@ def sebsync(
|
|
|
171
216
|
_verbose = verbose
|
|
172
217
|
_quiet = quiet
|
|
173
218
|
|
|
174
|
-
remote_ebooks =
|
|
219
|
+
remote_ebooks = get_remote_ebooks(opds, email, type)
|
|
175
220
|
if not remote_ebooks:
|
|
176
|
-
raise click.ClickException("
|
|
221
|
+
raise click.ClickException("OPDS download failed. Is email address correct?")
|
|
177
222
|
if _verbose:
|
|
178
|
-
click.echo(f"Found {len(remote_ebooks)} remote
|
|
223
|
+
click.echo(f"Found {len(remote_ebooks)} remote ebooks.")
|
|
179
224
|
|
|
180
225
|
local_ebooks = get_local_ebooks(downloads) | get_local_ebooks(books)
|
|
181
226
|
if _verbose:
|
|
182
|
-
click.echo(f"Found {len(local_ebooks)} local
|
|
227
|
+
click.echo(f"Found {len(local_ebooks)} local ebooks.")
|
|
183
228
|
|
|
184
229
|
for remote_ebook in remote_ebooks.values():
|
|
185
230
|
if local_ebook := local_ebooks.get(remote_ebook.id):
|
|
186
231
|
if remote_ebook.updated != local_ebook.modified:
|
|
232
|
+
_echo_status(local_ebook.path, Status.UPDATE)
|
|
187
233
|
download_ebook(remote_ebook.href, local_ebook.path)
|
|
188
234
|
else:
|
|
189
235
|
path = downloads / ebook_filename(remote_ebook)
|
|
236
|
+
_echo_status(path, Status.NEW)
|
|
190
237
|
download_ebook(remote_ebook.href, path)
|
|
191
238
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
click.secho(f"Extra: {local_ebook.path}", fg="yellow")
|
|
239
|
+
for local_ebook in local_ebooks.values():
|
|
240
|
+
if local_ebook.id not in remote_ebooks:
|
|
241
|
+
_echo_status(local_ebook.path, Status.EXTRA)
|
|
196
242
|
|
|
197
243
|
|
|
198
244
|
def main():
|
sebsync-1.0a2/PKG-INFO
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: sebsync
|
|
3
|
-
Version: 1.0a2
|
|
4
|
-
Summary: Synchronize Standard Ebooks catalog with local ebooks.
|
|
5
|
-
Home-page: https://github.com/pbryan/sebsync/
|
|
6
|
-
License: MIT
|
|
7
|
-
Keywords: ebook,downloader,standard,book
|
|
8
|
-
Author: sebsync authors
|
|
9
|
-
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Topic :: Other/Nonlisted Topic
|
|
18
|
-
Requires-Dist: click (>=8.1,<9.0)
|
|
19
|
-
Requires-Dist: requests (>=2.32,<3.0)
|
|
20
|
-
Project-URL: Documentation, https://github.com/pbryan/sebsync/tree/main/docs
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
|
-
# sebsync
|
|
24
|
-
|
|
25
|
-
sebsync is a simple script to synchronize all EPUBs from Standard Ebooks to your local
|
|
26
|
-
filesystem. It detects any new and updated titles and downloads them automatically.
|
|
27
|
-
|
|
28
|
-
## How it works
|
|
29
|
-
|
|
30
|
-
The `sebsync` script reads the Standard Ebooks OPDS catalog and reconciles their titles against
|
|
31
|
-
EPUBs in your local filesystem, downloading new ones or updating existing ones as necessary.
|
|
32
|
-
|
|
33
|
-
If you're member of the Standard Ebooks
|
|
34
|
-
[Patrons Circle](https://standardebooks.org/donate#patrons-circle), or have produced an ebook
|
|
35
|
-
for them in the past, you may access their [OPDS feed](https://standardebooks.org/feeds),
|
|
36
|
-
a catalog of all published titles. This allows new and updated titles to be easily recognized.
|
|
37
|
-
|
|
38
|
-
If a title is detected in the catalog that is not present in your local books or downloads
|
|
39
|
-
directory, then it will be downloaded into your downloads directory. If a new version of a book
|
|
40
|
-
is detected in the catalog that is already in your local books or download directory, then the
|
|
41
|
-
new version will be downloaded and stored in the same place it was found.
|
|
42
|
-
|
|
43
|
-
## Installation
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
pipx install sebsync
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Example usage
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
sebsync --email address@example.com --books /home/user/MyBooks --downloads /home/user/MyBooks/Downloads
|
|
53
|
-
```
|
|
54
|
-
|
sebsync-1.0a2/README.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# sebsync
|
|
2
|
-
|
|
3
|
-
sebsync is a simple script to synchronize all EPUBs from Standard Ebooks to your local
|
|
4
|
-
filesystem. It detects any new and updated titles and downloads them automatically.
|
|
5
|
-
|
|
6
|
-
## How it works
|
|
7
|
-
|
|
8
|
-
The `sebsync` script reads the Standard Ebooks OPDS catalog and reconciles their titles against
|
|
9
|
-
EPUBs in your local filesystem, downloading new ones or updating existing ones as necessary.
|
|
10
|
-
|
|
11
|
-
If you're member of the Standard Ebooks
|
|
12
|
-
[Patrons Circle](https://standardebooks.org/donate#patrons-circle), or have produced an ebook
|
|
13
|
-
for them in the past, you may access their [OPDS feed](https://standardebooks.org/feeds),
|
|
14
|
-
a catalog of all published titles. This allows new and updated titles to be easily recognized.
|
|
15
|
-
|
|
16
|
-
If a title is detected in the catalog that is not present in your local books or downloads
|
|
17
|
-
directory, then it will be downloaded into your downloads directory. If a new version of a book
|
|
18
|
-
is detected in the catalog that is already in your local books or download directory, then the
|
|
19
|
-
new version will be downloaded and stored in the same place it was found.
|
|
20
|
-
|
|
21
|
-
## Installation
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
pipx install sebsync
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Example usage
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
sebsync --email address@example.com --books /home/user/MyBooks --downloads /home/user/MyBooks/Downloads
|
|
31
|
-
```
|
|
File without changes
|
|
File without changes
|