a85b46bc70b04f9c915b67a3d70d72f51e070e7e
[redakcja.git] / src / depot / publishers / legimi.py
1 from datetime import date
2 import re
3 from django.conf import settings
4 from django.utils.html import escape
5 from django.utils.safestring import mark_safe
6 from librarian.functions import lang_code_3to2
7 from librarian.builders import EpubBuilder, MobiBuilder
8 from librarian.covers.marquise import MarquiseCover, LabelMarquiseCover
9 from .base import BasePublisher
10
11
12 class Legimi(BasePublisher):
13     BASE_URL = 'https://panel.legimi.pl'
14     LOGIN_URL = BASE_URL + '/publishers/membership'
15     UPLOAD_URL = BASE_URL + '/administration/upload/start'
16     CREATE_URL = BASE_URL + '/publishers/publications/create'
17     EDIT_URL = BASE_URL + '/publishers/publications/edit/%s'
18     EDIT_FILES_URL = BASE_URL + '/publishers/publications/editfiles/%s'
19     EDIT_SALE_URL = BASE_URL + '/publishers/publications/editsale/%s'
20
21     CATEGORIES = {
22         'Dla dzieci i młodzieży': 94,
23         'Książki dla dzieci': 15,
24         'Literatura młodzieżowa': 24,
25         'Kryminał': 29,
26         'Kryminał klasyczny': 31,
27         'Kryminał współczesny': 32,
28         'Kryminał historyczny': 30,
29         'default': 8886,
30         'Edukacja': 10,
31         'Słowniki i leksykony': 14,
32         'Encyklopedie': 13,
33         'Lektury': 11,
34         'Starożytność': 80,
35         'Barok': 83,
36         'Oświecenie': 84,
37         'Dwudziestolecie międzywojenne': 88,
38         'Średniowiecze': 81,
39         'Współczesność': 90,
40         'Modernizm': 87,
41         'Pozytywizm': 86,
42         'Renesans': 82,
43         'Romantyzm': 85,
44         'Młoda Polska': 89,
45         'Podręczniki': 52,
46         'Fantastyka i sci-fi': 25,
47         'Fantastyka': 26,
48         'Science fiction': 27,
49         'Języki obce': 59,
50         'Antyki i kolekcjonerstwo': 53,
51         'Astrologia i wróżbiarstwo': 54,
52         'Zdrowie i rodzina': 57,
53         'Hobby': 55,
54         'Medycyna i zdrowie': 58,
55         'Psychologiczne': 78,
56         'Styl': 56,
57         'Humanistyka': 97,
58         'Kultura i sztuka': 64,
59         'Film': 66,
60         'Muzyka': 65,
61         'Eseje literackie': 49,
62         'Historia': 60,
63         'Styl życia': 73,
64         'Wakacje i podróże': 69,
65         'Dla mężczyzn': 79,
66         'Sport': 76,
67         'Obyczajowe i romanse': 93,
68         'Humor': 68,
69         'Obyczajowe': 35,
70         'Powieść': 41,
71         'Powieść przygodowa': 42,
72         'Współczesna powieść przygodowa': 44,
73         'Historyczna powieść przygodowa': 43,
74         'Powieść historyczna': 46,
75         'Powieść psychologiczna': 47,
76         'Powieść religijna': 45,
77         'Romans': 36,
78         'Romans klasyczny': 38,
79         'Romans współczesny': 39,
80         'Literatura erotyczna': 40,
81         'Romans historyczny': 37,
82         'Dla kobiet': 77,
83         'Sensacja, thriller, horror': 91,
84         'Horror': 28,
85         'Sensacja': 33,
86         'Thriller': 34,
87         'Aktualności': 70,
88         'Czasopisma': 71,
89         'Literatura faktu, reportaże, biografie': 92,
90         'Literatura faktu': 16,
91         'Biografie': 17,
92         'Publicystyka': 20,
93         'Dzienniki': 19,
94         'Dokument, esej': 18,
95         'Historia literatury i krytyka literacka': 23,
96         'Literatura popularnonaukowa': 22,
97         'Reportaż': 21,
98         'Społeczno-polityczne': 72,
99         'Poezja i dramat': 95,
100         'Dramat': 48,
101         'Poezja': 50,
102         'Religia i duchowość': 51,
103         'Nauka i nowe technologie': 98,
104         'Nauka i technika': 61,
105         'Nauki ścisłe': 62,
106         'Nauki humanistyczne': 63,
107         'Technologia i Internet': 75,
108         'Specjalistyczne': 99,
109         'Biznes i finanse': 1,
110         'Ekonomia': 5,
111         'Finanse': 6,
112         'Zarządzanie': 3,
113         'Marketing': 2,
114         'Rozwój osobisty': 7,
115         'Kariera i sukces zawodowy': 8,
116         'Psychologia, motywacja': 9,
117         'PR': 4,
118         'Prawo': 67,
119         'Branżowe': 74,
120     }
121
122     def login(self):
123         self._session.post(
124             self.LOGIN_URL,
125             data={
126                 'ValidationTrue': 'true',
127                 'UserName': self.username,
128                 'Password': self.password,
129             })
130
131     def can_publish(self, shop, book):
132         meta = book.wldocument(librarian2=True).meta
133         d = {
134             'errors': [],
135             'warnings': [],
136             'info': []
137         }
138         if meta.thema_main or meta.thema:
139             if meta.thema_main:
140                 comment = "w kategorii <b><tt>{code}</tt></b>".format(
141                     code=escape(meta.thema_main)
142                 )
143                 if meta.thema:
144                     comment += " oraz: " + ", ".join(
145                         "<b><tt>{code}</tt></b>".format(code=escape(t))
146                         for t in meta.thema
147                     )
148                 d['info'].append(mark_safe(comment))
149             elif meta.thema:
150                 d['info'].append(mark_safe(
151                     "w kategorii " + ", ".join(
152                         "<b><tt>{code}</tt></b>".format(code=escape(t))
153                         for t in meta.thema
154                     )
155                 ))
156                 d['warnings'].append('Brak głównej kategorii Thema')
157         else:
158             d['errors'].append('Brak kategorii Thema.')
159         return d
160
161     def list(self):
162         return self.session.get('https://wydawca.legimi.com/publishers/publications')
163
164     def upload(self, content):
165         response = self.session.post(
166             self.UPLOAD_URL,
167             files={
168                 "files": content,
169             })
170         model = response.json()['model']
171         return {
172             "name": model['Name'],
173             "token": model['Token'],
174             "url": model['Url'],
175         }
176
177     def send_book(self, shop, book, changes=None):
178         wlbook = book.wldocument(librarian2=True, changes=changes)
179         meta = wlbook.meta
180
181         cover = LabelMarquiseCover(meta, width=1200).output_file()
182         texts = shop.get_texts()
183         epub_file = EpubBuilder(
184             cover=MarquiseCover,
185             fundraising=texts,
186             base_url='file://' + book.gallery_path() + '/'
187         ).build(wlbook).get_file()
188         mobi_file = MobiBuilder(
189             cover=MarquiseCover,
190             fundraising=texts,
191             base_url='file://' + book.gallery_path() + '/'
192         ).build(wlbook).get_file()
193
194         thema = []
195         if meta.thema_main:
196             thema.append(meta.thema_main)
197         thema.extend(meta.thema)
198
199         book_data = {
200             "Title": meta.title,
201             "Author": ", ".join(p.readable() for p in meta.authors),
202             "Year": str(date.today().year),
203
204             'GenreId': str(self.get_genre(wlbook)),
205             'themaCategories': ';'.join(thema),
206             'thema-search': '',
207             'Isbn': '',
208             'LanguageLocale': lang_code_3to2(meta.language),
209
210             'Description': self.get_description(wlbook, shop.description_add),
211         }
212         if meta.isbn_html:
213             isbn = meta.isbn_html
214             if isbn.upper().startswith(('ISBN ', 'ISBN-')):
215                 isbn = isbn[5:]
216             isbn = isbn.strip()
217             book_data['Isbn'] = isbn
218
219         files_data = {}
220
221         cover_data = self.upload(
222             (meta.url.slug + '.jpg', cover.get_file(), 'image/jpeg')
223         )
224         book_data.update({
225             "Cover.Name": cover_data['name'],
226             "Cover.Token": cover_data['token'],
227             "Cover.Url": cover_data['url'],
228         })
229
230         epub_data = self.upload(
231             (meta.url.slug + '.epub', epub_file, 'application/epub+zip')
232         )
233         files_data.update({
234             'BookEpub.Token': epub_data['token'],
235             'BookEpub.Name': epub_data['name'],
236             'SampleEpubType': 'Generation',
237         })
238
239         mobi_data = self.upload(
240             (meta.url.slug + '.mobi', mobi_file, 'application/x-mobipocket-ebook')
241         )
242         files_data.update({
243             'BookMobi.Token': mobi_data['token'],
244             'BookMobi.Name': mobi_data['name'],
245         })
246
247         if book.legimi_id:
248             self.edit(
249                 book.legimi_id,
250                 book_data
251             )
252             self.edit_files(
253                 book.legimi_id,
254                 files_data
255             )
256         else:
257             legimi_id = self.create_book(book_data, files_data)
258             if legimi_id:
259                 book.legimi_id = legimi_id
260                 book.save(update_fields=['legimi_id'])
261
262         self.edit_sale(book)
263
264     def get_genre(self, wlbook):
265         if wlbook.meta.legimi and wlbook.meta.legimi in self.CATEGORIES:
266             return self.CATEGORIES[wlbook.meta.legimi]
267         for epoch in wlbook.meta.epochs:
268             if epoch in self.CATEGORIES:
269                 return self.CATEGORIES[epoch]
270         return self.CATEGORIES['Lektury']
271
272     def create_book(self, book_data, files_data):
273         data = {
274             'createValidationTrue': 'true',
275             'PublisherId': self.publisher_handle,
276             'IsLibraryPass': 'False',
277
278             'SamplesGenerationType': 'Quantity',
279             'SamplesGenerationPercent': '10',
280
281             'EnterToTheMarketType': 'No',
282             'EnterToTheMarketDate': '',
283             'HidingDate': '',
284             'SalesNoLimitOption': 'false',
285             'SalesNoLimitKindle': 'false',
286             'SalesInStoreEbookGrossValue': '0,00',
287             'SalesPromotion': 'False',
288             'SalesPromotionGrossValue': '0,00',
289             'SalesPromotionDatesRange.DateStart': '',
290             'SalesPromotionDatesRange.DateEnd': '',
291         }
292
293         for form in 'Epub', 'Mobi', 'Pdf':
294             data.update({
295                 f'Book{form}.Token': '',
296                 f'Book{form}.Name': '',
297                 f'Book{form}.StorageName': '',
298                 f'Book{form}.Order': '',
299
300                 f'Sample{form}Type': 'Files',
301                 f'Sample{form}.Token': '',
302                 f'Sample{form}.Name': '',
303                 f'Sample{form}.StorageName': '',
304                 f'Sample{form}.Order': '',
305             })
306
307         data.update(book_data)
308         data.update(files_data)
309
310         response = self.session.post(self.CREATE_URL, data=data)
311         m = re.search(r'/(\d+)$', response.url)
312         if m is not None:
313             return m.group(1)
314
315     def edit(self, legimi_id, data):
316         current = {
317             'ValidationTrue': 'true',
318             'Id': legimi_id
319         }
320
321         current.update(data)
322
323         self.session.post(
324             self.EDIT_URL % legimi_id,
325             data=current
326         )
327
328     def edit_files(self, legimi_id, files_data):
329         current = {
330             'ValidationTrue': 'true',
331             'Id': legimi_id,
332             'SamplesGenerationType': 'Quantity',
333             'SamplesGenerationPercent': '10',
334         }
335
336         for form in 'Epub', 'Mobi', 'Pdf':
337             current.update({
338                 f'Book{form}.Token': '',
339                 f'Book{form}.Name': '',
340                 f'Book{form}.StorageName': '',
341                 f'Book{form}.Order': '',
342
343                 f'Sample{form}.Type': 'Files',
344                 f'Sample{form}.Token': '',
345                 f'Sample{form}.Name': '',
346                 f'Sample{form}.StorageName': '',
347                 f'Sample{form}.Order': '',
348             })
349
350         current.update(files_data)
351
352         response = self.session.post(
353             self.EDIT_FILES_URL % legimi_id,
354             data=current
355         )
356
357     def edit_sale(self, book):
358         assert book.legimi_id
359
360         words = book.wldocument().get_statistics()['total']['words_with_fn']
361
362         price = settings.LEGIMI_SMALL_PRICE
363         if words > settings.LEGIMI_SMALL_WORDS:
364             price = settings.LEGIMI_BIG_PRICE
365
366         abo = 'true' if words > settings.LEGIMI_BIG_WORDS else 'false'
367
368         data = {
369             'ValidationTrue': 'true',
370             'Id': book.legimi_id,
371             'SalesPromotionId': "0",
372             'IsLibraryPass': "False",
373             'OriginalEnterToTheMarketType': "No",
374             'OriginalHidingDate': "",
375             'OriginalEnterToTheMarketDate': "",
376             'EnterToTheMarketType': "Yes",
377             'EnterToTheMarketDate': "",
378             'HidingDate': "",
379             'SalesNoLimitOption': abo,
380             'SalesNoLimitKindle': abo,
381             'SalesInStoreEbookGrossValue': f'{price},00',
382             'SalesPromotion': "False",
383             'SalesPromotionGrossValue': "0,00",
384             'SalesPromotionDatesRange.DateStart': "",
385             'SalesPromotionDatesRange.DateEnd': "",
386         }
387
388         self.session.post(
389             self.EDIT_SALE_URL % book.legimi_id,
390             data=data
391         )