-
Notifications
You must be signed in to change notification settings - Fork 0
/
shelfslide.py
264 lines (214 loc) · 8.75 KB
/
shelfslide.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
import os
import yaml
import json
import requests
import sys
import random
import subprocess
import logging
import time
import argparse
import glob
## own modules
sys.path.append("..")
import src.book as Book
import src.display as Display
import src.slideshow as Slideshow
SLIDESHOW_MIN_SLEEP = 300 # min 5min sleep
##
# @brief Download a cover image from a URL
def download_covers(url, path):
try:
response = requests.get(url)
response.raise_for_status() # Check for any HTTP errors
with open(path, 'wb') as file:
file.write(response.content)
except requests.exceptions.RequestException as e:
logging.error(f"Failed to download image: {e}")
##
# @brief Generate a cover filename from the author and title
def generate_coverfilename(author, title):
author = author.replace(" ", "-").lower()
title = title.replace(" ", "-").lower()
return f"{author}_{title}.jpg"
##
# @brief Sort the book list according to the preferred mode
def sort_books(books, mode):
if mode == "desc":
books.sort(key=lambda x: x.date, reverse=True)
elif mode == "asc":
books.sort(key=lambda x: x.date, reverse=False)
elif mode == "rand":
random.shuffle(books)
else:
# do nothing
return books
newBk = books
return newBk
##
# Check if the cover was already downloaded once
def cover_exists(name, dir):
files = glob.glob(dir+name)
if len(files) == 0:
return False
else:
return True
##
# @brief Load the book library from a json file
def load_bookLibrary(file, cover_dir, offlineOnly):
list = []
for entry in file['read']:
cover = entry['cover']
bk = Book.Book(entry['title'], entry['author'], cover_dir+"/"+cover['url'], entry['date'])
# if the book has an online cover, download it, the path needs to be updated
if (cover['urlType'] == "link") and (not offlineOnly):
# check if the file should be downloaded
if not cover_exists(generate_coverfilename(entry['author'], entry['title']), cover_dir + "/cache/"):
download_covers(cover['url'], cover_dir + "/cache/" + generate_coverfilename(entry['author'], entry['title']))
bk.set_cover(cover_dir + "/cache/" + generate_coverfilename(entry['author'], entry['title']))
list.append(bk)
return list
##
# @brief Configure the argument parser
def config_args():
parser = argparse.ArgumentParser(prog='ShelfSlide',
description='Show your read books on an e-paper display',
epilog='For more information please visit github.com/bitSheriff/ShelfSlide')
parser.add_argument('--offline', '-o', action='store_true', help='Option to run ShelfSlide offline, without downloading covers')
parser.add_argument('--clear', '-c', action='store_true', help='Just clear the display and exit')
parser.add_argument('--time', '-t', default=0, help='Time between slides in seconds')
parser.add_argument('--dryrun', '-d', action='store_true', help='Dry run, test if all given links are valid')
parser.add_argument('--update', '-u', action='store_true', help='Update the application from the git repository')
parser.add_argument('--verbose', '-v', action='store_true', help='Log all happenings')
parser.add_argument('--show', '-s', default="", help='Show given picture')
parser.add_argument('--logo', '-l', action='store_true', help='Just show the logo and exit')
# return the parsed arguments
return parser.parse_args()
##
# Clean the downloaded covers
def clean_cache(cover_dir):
path = cover_dir + "/cache/"
for file in os.listdir(path):
file_path = os.path.join(path, file)
if os.path.isfile(file_path):
os.remove(file_path)
def file_is_media(path):
media_extensions = ["jpeg", "jpg", "png"]
# check if the extension is one of the allowed one
for ext in media_extensions:
if ext in path:
return True
# extension not found, so no allowed media file
return False
def load_simple_bookLibrary(cover_dir):
book_list = []
for file in os.listdir(cover_dir):
file_path = os.path.join(cover_dir, file)
if file_is_media(file_path):
bk = Book.Book(title = "", author = "", cover = file_path, date = "")
book_list.append(bk)
return book_list
##
# @brief Update the book library from a git repository
def update_bookLibrary(book_dir,cover_dir, is_git, slide_mode, offlineOnly, clean, simpleMode):
if is_git:
original_directory = os.getcwd()
os.chdir(book_dir)
subprocess.run(["git", "pull"])
os.chdir(original_directory) # change back to original directory
if simpleMode:
return load_simple_bookLibrary(cover_dir)
with open(book_dir+"/books.json",'r') as file:
books_file = json.load(file)
# check if the old covers should be removed before downloading the new
if clean:
clean_cache(cover_dir)
book_list = load_bookLibrary(books_file, cover_dir, offlineOnly)
book_list = sort_books(book_list, slide_mode)
return book_list
def error_exit(display, text):
logging.error(text)
display.display_logo()
sys.exit(0)
def logging_config(verbose):
# set the logging to the console
if verbose:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.CRITICAL)
# set the logging to a file
logging.basicConfig(filename='shelfslide.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
##
# @brief Main function
def main():
# configure the arg parser
parser = config_args()
# configure the logging
logging_config(parser.verbose)
# load the config file
with open('config.yaml', 'r') as file:
config_file = yaml.safe_load(file)
# configure the display
display = Display.display( config_file['display']['type'],
config_file['display']['width'],
config_file['display']['height'],
config_file['display']['colors'],
config_file['display']['rot_inv'])
# check if the update flag is set
if parser.update:
subprocess.run(["git", "pull"])
print("Updated ShelfSlide\n Please restart the application")
error_exit(display, "")
# check if the dryrun flag is set
if parser.dryrun:
raise NotImplementedError("Dry run not implemented yet")
# check if the clear flag is set
if parser.clear:
# clear the display and exit
display.display_clear()
sys.exit(0)
# check if the logo flag is set
if parser.logo:
# show the logo and exit
display.display_logo()
sys.exit(0)
# check if the show flag is set
if parser.show != "":
# show the given picture
display.display_image(parser.show)
sys.exit(0)
# get the books
book_list = update_bookLibrary( config_file['books']['dir'],
str(str(config_file['books']['dir']) + "/media"),
config_file['books']['git'],
config_file['slideshow']['mode'],
parser.offline,
config_file['books']['clean'],
config_file['books']['simpleMode'])
slideshow_sleep = min( config_file['slideshow']['interval'], SLIDESHOW_MIN_SLEEP)
# override the configured tme if the user specified one (allowed to be less than 5min)
if parser.time != 0:
slideshow_sleep = parser.time
# init the slideshow
slideshow = Slideshow.slideshow(int(slideshow_sleep), book_list, display)
# main part of the application
while True:
# run the slideshow
slideshow.run()
# check if an update is needed
if slideshow.interrupt_update:
# update the book list
book_list = update_bookLibrary( config_file['books']['dir'],
str(str(config_file['books']['dir']) + "/media"),
config_file['books']['git'],
config_file['slideshow']['mode'],
parser.offline,
config_file['books']['clean'],
config_file['books']['simpleMode'])
# update the slideshow with the new book list
slideshow.update_done(book_list)
##
# @brief Main function
if __name__ == "__main__":
os.chdir(os.path.dirname(os.path.abspath(__file__)))
main()