|
1 | 1 | import json |
2 | 2 | import os |
| 3 | +import platform |
3 | 4 | from typing import List, TextIO, Tuple, Union |
4 | 5 |
|
5 | 6 | import click |
@@ -32,29 +33,34 @@ def __GetBookAuthor(book: dict) -> str: |
32 | 33 | return ' & '.join(authors) |
33 | 34 |
|
34 | 35 |
|
35 | | -def __SanitizeFileName(fileName: str) -> str: |
| 36 | +def __SanitizeString(string: str) -> str: |
36 | 37 | result = '' |
37 | | - for c in fileName: |
| 38 | + for c in string: |
38 | 39 | if c.isalnum() or ' ,;.!(){}[]#$\'-+@_'.find(c) >= 0: |
39 | 40 | result += c |
40 | 41 |
|
41 | 42 | result = result.strip(' .') |
42 | | - result = result[ |
43 | | - :100 |
44 | | - ] # Limit the length -- mostly because of Windows. It would be better to do it on the full path using MAX_PATH. |
| 43 | + if platform.system() == 'Windows': |
| 44 | + # Limit the length -- mostly because of Windows. It would be better to do it on the full path using MAX_PATH. |
| 45 | + result = result[:100] |
45 | 46 | return result |
46 | 47 |
|
47 | 48 |
|
48 | | -def __MakeFileNameForBook(bookMetadata: dict) -> str: |
| 49 | +def __MakeFileNameForBook(bookMetadata: dict, formatStr: str) -> str: |
49 | 50 | '''filename without extension''' |
50 | 51 | fileName = '' |
51 | | - author = __GetBookAuthor(bookMetadata) |
52 | | - if len(author) > 0: |
53 | | - fileName = author + ' - ' |
54 | | - fileName += bookMetadata['Title'] |
55 | | - fileName = __SanitizeFileName(fileName) |
56 | | - # Append a portion of revisionId to prevent name collisions. |
57 | | - return f"{fileName} {bookMetadata['RevisionId'][:8]}" |
| 52 | + author = __SanitizeString(__GetBookAuthor(bookMetadata)) |
| 53 | + title = __SanitizeString(bookMetadata['Title']) |
| 54 | + |
| 55 | + return formatStr.format_map( |
| 56 | + { |
| 57 | + **bookMetadata, |
| 58 | + 'Author': author, |
| 59 | + 'Title': title, |
| 60 | + # Append a portion of revisionId to prevent name collisions. |
| 61 | + 'ShortRevisionId': bookMetadata['RevisionId'][:8], |
| 62 | + } |
| 63 | + ) |
58 | 64 |
|
59 | 65 |
|
60 | 66 | def __GetBookMetadata(entitlement: dict) -> Tuple[dict, BookType]: |
@@ -163,7 +169,12 @@ def Login(user: User, password: str, captcha: str) -> None: |
163 | 169 | kobo.Login(user.Email, password, captcha) |
164 | 170 |
|
165 | 171 |
|
166 | | -def GetBookOrBooks(user: User, outputPath: str, productId: str = '') -> Union[None, str]: |
| 172 | +def GetBookOrBooks( |
| 173 | + user: User, |
| 174 | + outputPath: str, |
| 175 | + formatStr: str = r'{Author} - {Title} {ShortRevisionId}', |
| 176 | + productId: str = '', |
| 177 | +) -> Union[None, str]: |
167 | 178 | """ |
168 | 179 | download 1 or all books to file |
169 | 180 | returns output filepath if identifier is passed, otherwise returns None |
@@ -192,7 +203,7 @@ def GetBookOrBooks(user: User, outputPath: str, productId: str = '') -> Union[No |
192 | 203 | click.echo('Skipping subscribtion entity') |
193 | 204 | continue |
194 | 205 |
|
195 | | - fileName = __MakeFileNameForBook(bookMetadata) |
| 206 | + fileName = __MakeFileNameForBook(bookMetadata, formatStr) |
196 | 207 | if book_type == BookType.EBOOK: |
197 | 208 | # Audiobooks go in sub-directories |
198 | 209 | # but epub files go directly in outputPath |
|
0 commit comments