mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 07:13:02 +00:00 
			
		
		
		
	Merge branch 'master' into Develop
This commit is contained in:
		| @@ -89,8 +89,9 @@ Refer to the Wiki for additional installation examples: [manual installation](ht | ||||
|  | ||||
| ## Requirements | ||||
|  | ||||
| - Python 3.5+ | ||||
| - Python 3.7+ | ||||
| - [Imagemagick](https://imagemagick.org/script/download.php) for cover extraction from EPUBs (Windows users may need to install [Ghostscript](https://ghostscript.com/releases/gsdnld.html) for PDF cover extraction) | ||||
| - Windows users need to install [libmagic for 32bit python](https://gnuwin32.sourceforge.net/downlinks/file.php) or [libmagic for 64bit python](https://github.com/nscaife/file-windows/releases/tag/20170108), depending on the python version; The files need to be installed in path (e.g. script folder of your Calibre-Web venv, or in the root folder of Calibre-Web | ||||
| - Optional: [Calibre desktop program](https://calibre-ebook.com/download) for on-the-fly conversion and metadata editing (set "calibre's converter tool" path on the setup page) | ||||
| - Optional: [Kepubify tool](https://github.com/pgaskin/kepubify/releases/latest) for Kobo device support (place the binary in `/opt/kepubify` on Linux or `C:\Program Files\kepubify` on Windows) | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								cps.py
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								cps.py
									
									
									
									
									
								
							| @@ -30,19 +30,15 @@ from cps.main import main | ||||
|  | ||||
| def hide_console_windows(): | ||||
|     import ctypes | ||||
|     import os | ||||
|  | ||||
|     hwnd = ctypes.windll.kernel32.GetConsoleWindow() | ||||
|     if hwnd != 0: | ||||
|         try: | ||||
|             import win32process | ||||
|         except ImportError: | ||||
|             print("To hide console window install 'pywin32' using 'pip install pywin32'") | ||||
|             return | ||||
|         ctypes.windll.user32.ShowWindow(hwnd, 0) | ||||
|         ctypes.windll.kernel32.CloseHandle(hwnd) | ||||
|         _, pid = win32process.GetWindowThreadProcessId(hwnd) | ||||
|         os.system('taskkill /PID ' + str(pid) + ' /f') | ||||
|     kernel32 = ctypes.WinDLL('kernel32') | ||||
|     user32 = ctypes.WinDLL('user32') | ||||
|  | ||||
|     SW_HIDE = 0 | ||||
|  | ||||
|     hWnd = kernel32.GetConsoleWindow() | ||||
|     if hWnd: | ||||
|         user32.ShowWindow(hWnd, SW_HIDE) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|   | ||||
| @@ -70,8 +70,8 @@ mimetypes.add_type('image/vnd.djv', '.djv') | ||||
| mimetypes.add_type('image/vnd.djv', '.djvu') | ||||
| mimetypes.add_type('application/mpeg', '.mpeg') | ||||
| mimetypes.add_type('audio/mpeg', '.mp3') | ||||
| mimetypes.add_type('application/mp4', '.m4a') | ||||
| mimetypes.add_type('application/mp4', '.m4b') | ||||
| mimetypes.add_type('audio/x-m4a', '.m4a') | ||||
| mimetypes.add_type('audio/x-m4a', '.m4b') | ||||
| mimetypes.add_type('audio/ogg', '.ogg') | ||||
| mimetypes.add_type('application/ogg', '.oga') | ||||
| mimetypes.add_type('text/css', '.css') | ||||
|   | ||||
| @@ -29,8 +29,9 @@ log = logger.create() | ||||
|  | ||||
| try: | ||||
|     import magic | ||||
|     error = None | ||||
| except ImportError as e: | ||||
|     log.error("Cannot import python-magic, checking uploaded file metadata will not work: %s", e) | ||||
|     error = "Cannot import python-magic, checking uploaded file metadata will not work: {}".format(e) | ||||
|  | ||||
|  | ||||
| def get_temp_dir(): | ||||
| @@ -46,12 +47,15 @@ def del_temp_dir(): | ||||
|  | ||||
|  | ||||
| def validate_mime_type(file_buffer, allowed_extensions): | ||||
|     if error: | ||||
|         log.error(error) | ||||
|         return False | ||||
|     mime = magic.Magic(mime=True) | ||||
|     allowed_mimetypes =list() | ||||
|     allowed_mimetypes = list() | ||||
|     for x in allowed_extensions: | ||||
|         try: | ||||
|             allowed_mimetypes.append(mimetypes.types_map["." + x]) | ||||
|         except KeyError as e: | ||||
|         except KeyError: | ||||
|             log.error("Unkown mimetype for Extension: {}".format(x)) | ||||
|     tmp_mime_type = mime.from_buffer(file_buffer.read()) | ||||
|     file_buffer.seek(0) | ||||
| @@ -66,6 +70,5 @@ def validate_mime_type(file_buffer, allowed_extensions): | ||||
|                     return True | ||||
|         except: | ||||
|             file_buffer.seek(0) | ||||
|             pass | ||||
|      | ||||
|     log.error("Mimetype '{}' not found in allowed types".format(tmp_mime_type)) | ||||
|     return False | ||||
|   | ||||
| @@ -562,7 +562,7 @@ def move_files_on_change(calibre_path, new_author_dir, new_titledir, localbook, | ||||
|             if not os.path.isdir(new_path): | ||||
|                 os.makedirs(new_path) | ||||
|             shutil.move(original_filepath, os.path.join(new_path, db_filename)) | ||||
|             log.debug("Moving title: %s to %s/%s", original_filepath, new_path) | ||||
|             log.debug("Moving title: %s to %s", original_filepath, new_path) | ||||
|         else: | ||||
|             # Check new path is not valid path | ||||
|             if not os.path.exists(new_path): | ||||
|   | ||||
| @@ -269,10 +269,18 @@ class TaskConvert(CalibreTask): | ||||
|                                '--with-library', library_path] | ||||
|                 p = process_open(opf_command, quotes, my_env) | ||||
|                 p.wait() | ||||
|                 path_tmp_opf = os.path.join(tmp_dir, "metadata_" + str(uuid4()) + ".opf") | ||||
|                 with open(path_tmp_opf, 'w') as fd: | ||||
|                     copyfileobj(p.stdout, fd) | ||||
|  | ||||
|                 check = p.returncode | ||||
|                 calibre_traceback = p.stderr.readlines() | ||||
|                 if check == 0: | ||||
|                     path_tmp_opf = os.path.join(tmp_dir, "metadata_" + str(uuid4()) + ".opf") | ||||
|                     with open(path_tmp_opf, 'w') as fd: | ||||
|                         copyfileobj(p.stdout, fd) | ||||
|                 else: | ||||
|                     error_message = "" | ||||
|                     for ele in calibre_traceback: | ||||
|                         if not ele.startswith('Traceback') and not ele.startswith('  File'): | ||||
|                             error_message = N_("Calibre failed with error: %(error)s", error=ele) | ||||
|                     return check, error_message | ||||
|             quotes = [1, 2, 4, 6] | ||||
|             command = [config.config_converterpath, (file_path + format_old_ext), | ||||
|                        (file_path + format_new_ext)] | ||||
|   | ||||
| @@ -43,6 +43,3 @@ comicapi>=2.2.0,<3.3.0 | ||||
|  | ||||
| # Kobo integration | ||||
| jsonschema>=3.2.0,<4.24.0 | ||||
|  | ||||
| # Hide console Window on Windows | ||||
| pywin32>=220,<310 ; sys_platform == 'win32' | ||||
|   | ||||
| @@ -37,20 +37,20 @@ | ||||
|       <div class="row"> | ||||
|         <div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;"> | ||||
|              | ||||
|             <p class='text-justify attribute'><strong>Start Time: </strong>2024-07-17 20:06:46</p> | ||||
|             <p class='text-justify attribute'><strong>Start Time: </strong>2024-07-18 20:53:44</p> | ||||
|              | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|         <div class="col-xs-6 col-md-6 col-sm-offset-3"> | ||||
|              | ||||
|             <p class='text-justify attribute'><strong>Stop Time: </strong>2024-07-18 03:16:31</p> | ||||
|             <p class='text-justify attribute'><strong>Stop Time: </strong>2024-07-19 03:48:09</p> | ||||
|              | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|         <div class="col-xs-6 col-md-6 col-sm-offset-3"> | ||||
|            <p class='text-justify attribute'><strong>Duration: </strong>5h 58 min</p> | ||||
|            <p class='text-justify attribute'><strong>Duration: </strong>5h 43 min</p> | ||||
|         </div> | ||||
|       </div> | ||||
|       </div> | ||||
| @@ -1619,13 +1619,13 @@ | ||||
|  | ||||
|     <tr id="su" class="passClass"> | ||||
|         <td>TestEditAuthors</td> | ||||
|         <td class="text-center">8</td> | ||||
|         <td class="text-center">8</td> | ||||
|         <td class="text-center">9</td> | ||||
|         <td class="text-center">9</td> | ||||
|         <td class="text-center">0</td> | ||||
|         <td class="text-center">0</td> | ||||
|         <td class="text-center">0</td> | ||||
|         <td class="text-center"> | ||||
|             <a onclick="showClassDetail('c14', 8)">Detail</a> | ||||
|             <a onclick="showClassDetail('c14', 9)">Detail</a> | ||||
|         </td> | ||||
|     </tr> | ||||
|  | ||||
| @@ -1687,7 +1687,7 @@ | ||||
|      | ||||
|         <tr id='pt14.7' class='hiddenRow bg-success'> | ||||
|             <td> | ||||
|                 <div class='testcase'>TestEditAuthors - test_rename_author_accent_onupload</div> | ||||
|                 <div class='testcase'>TestEditAuthors - test_rename_author_emphasis_mark_onupload</div> | ||||
|             </td> | ||||
|             <td colspan='6' align='center'>PASS</td> | ||||
|         </tr> | ||||
| @@ -1702,6 +1702,15 @@ | ||||
|         </tr> | ||||
|      | ||||
|      | ||||
|      | ||||
|         <tr id='pt14.9' class='hiddenRow bg-success'> | ||||
|             <td> | ||||
|                 <div class='testcase'>TestEditAuthors - test_rename_tag_emphasis_mark_onupload</div> | ||||
|             </td> | ||||
|             <td colspan='6' align='center'>PASS</td> | ||||
|         </tr> | ||||
|      | ||||
|      | ||||
|  | ||||
|  | ||||
|     <tr id="su" class="passClass"> | ||||
| @@ -2635,11 +2644,11 @@ IndexError: list index out of range</pre> | ||||
|      | ||||
|  | ||||
|  | ||||
|     <tr id="su" class="failClass"> | ||||
|     <tr id="su" class="passClass"> | ||||
|         <td>TestKoboSync</td> | ||||
|         <td class="text-center">12</td> | ||||
|         <td class="text-center">11</td> | ||||
|         <td class="text-center">1</td> | ||||
|         <td class="text-center">12</td> | ||||
|         <td class="text-center">0</td> | ||||
|         <td class="text-center">0</td> | ||||
|         <td class="text-center">0</td> | ||||
|         <td class="text-center"> | ||||
| @@ -2730,31 +2739,11 @@ IndexError: list index out of range</pre> | ||||
|      | ||||
|      | ||||
|      | ||||
|         <tr id="ft29.10" class="none bg-danger"> | ||||
|         <tr id='pt29.10' class='hiddenRow bg-success'> | ||||
|             <td> | ||||
|                 <div class='testcase'>TestKoboSync - test_sync_shelf</div> | ||||
|             </td> | ||||
|             <td colspan='6'> | ||||
|                 <div class="text-center"> | ||||
|                     <a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft29.10')">FAIL</a> | ||||
|                 </div> | ||||
|                 <!--css div popup start--> | ||||
|                 <div id="div_ft29.10" class="popup_window test_output" style="display:block;"> | ||||
|                     <div class='close_button pull-right'> | ||||
|                         <button type="button" class="close" aria-label="Close" onfocus="this.blur();" | ||||
|                                 onclick="document.getElementById('div_ft29.10').style.display='none'"><span | ||||
|                                 aria-hidden="true">×</span></button> | ||||
|                     </div> | ||||
|                     <div class="text-left pull-left"> | ||||
|                         <pre class="text-left">Traceback (most recent call last): | ||||
|   File "/home/ozzie/Development/calibre-web-test/test/test_kobo_sync.py", line 368, in test_sync_shelf | ||||
|     self.assertEqual(1, len(data), data) | ||||
| AssertionError: 1 != 0 : []</pre> | ||||
|                     </div> | ||||
|                     <div class="clearfix"></div> | ||||
|                 </div> | ||||
|                 <!--css div popup end--> | ||||
|             </td> | ||||
|             <td colspan='6' align='center'>PASS</td> | ||||
|         </tr> | ||||
|      | ||||
|      | ||||
| @@ -5624,9 +5613,9 @@ ModuleNotFoundError: No module named 'build_release'</pre> | ||||
|  | ||||
|     <tr id='total_row' class="text-center bg-grey"> | ||||
|         <td>Total</td> | ||||
|         <td>498</td> | ||||
|         <td>485</td> | ||||
|         <td>1</td> | ||||
|         <td>499</td> | ||||
|         <td>487</td> | ||||
|         <td>0</td> | ||||
|         <td>2</td> | ||||
|         <td>10</td> | ||||
|         <td> </td> | ||||
| @@ -5656,7 +5645,7 @@ ModuleNotFoundError: No module named 'build_release'</pre> | ||||
|            | ||||
|             <tr> | ||||
|               <th>Platform</th> | ||||
|               <td>Linux 6.5.0-41-generic #41~22.04.2-Ubuntu SMP PREEMPT_DYNAMIC Mon Jun  3 11:32:55 UTC 2 x86_64 x86_64</td> | ||||
|               <td>Linux 6.5.0-44-generic #44~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Jun 18 14:36:16 UTC 2 x86_64 x86_64</td> | ||||
|               <td>Basic</td> | ||||
|             </tr> | ||||
|            | ||||
| @@ -6160,7 +6149,7 @@ ModuleNotFoundError: No module named 'build_release'</pre> | ||||
| </div> | ||||
|  | ||||
| <script> | ||||
|     drawCircle(485, 1, 2, 10); | ||||
|     drawCircle(487, 0, 2, 10); | ||||
|     showCase(5); | ||||
| </script> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Ozzie Isaacs
					Ozzie Isaacs