1# Copyright (C) 2007-2020 Damon Lynch <damonlynch@gmail.com> 2 3# This file is part of Rapid Photo Downloader. 4# 5# Rapid Photo Downloader is free software: you can redistribute it and/or 6# modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation, either version 3 of the License, or 9# (at your option) any later version. 10# 11# Rapid Photo Downloader is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with Rapid Photo Downloader. If not, 18# see <http://www.gnu.org/licenses/>. 19 20__author__ = 'Damon Lynch' 21__copyright__ = "Copyright 2007-2020, Damon Lynch" 22 23from enum import (Enum, IntEnum) 24from PyQt5.QtCore import Qt 25from PyQt5.QtGui import QFont, QFontMetrics, QColor 26 27PROGRAM_NAME = "Rapid Photo Downloader" 28logfile_name = 'rapid-photo-downloader.log' 29 30remote_versions_file = 'https://www.damonlynch.net/rapid/version.json' 31 32# If set to True, the ability to check for a new version will be removed 33# from the user interface and disabled in program logic. 34disable_version_check = False 35 36 37class CheckNewVersionDialogResult(IntEnum): 38 download = 1 39 do_not_download = 2 40 skip = 3 41 open_website = 4 42 43 44class CheckNewVersionDialogState(IntEnum): 45 check = 1 46 prompt_for_download = 2 47 open_website = 3 48 failed_to_contact = 4 49 have_latest_version = 5 50 51 52class ConflictResolution(IntEnum): 53 skip = 1 54 add_identifier = 2 55 56 57class ErrorType(Enum): 58 critical_error = 1 59 serious_error = 2 60 warning = 3 61 62 63class PresetPrefType(Enum): 64 preset_photo_subfolder = 1 65 preset_video_subfolder = 2 66 preset_photo_rename = 3 67 preset_video_rename = 4 68 69 70class PresetClass(Enum): 71 builtin = 1 72 custom = 2 73 new_preset = 3 74 remove_all = 4 75 update_preset = 5 76 edited = 6 77 start_editor = 7 78 79 80class DownloadStatus(Enum): 81 # going to try to download it 82 download_pending = 1 83 84 # downloaded successfully 85 downloaded = 2 86 87 # downloaded ok but there was a warning 88 downloaded_with_warning = 3 89 90 # downloaded ok, but the file was not backed up, or had a problem 91 # (overwrite or duplicate) 92 backup_problem = 4 93 94 # has not yet been downloaded (but might be if the user chooses) 95 not_downloaded = 5 96 97 # tried to download but failed, and the backup failed or had an error 98 download_and_backup_failed = 6 99 100 # tried to download but failed 101 download_failed = 7 102 103 104Downloaded = (DownloadStatus.downloaded, 105 DownloadStatus.downloaded_with_warning, 106 DownloadStatus.backup_problem) 107 108 109DownloadWarning = {DownloadStatus.downloaded_with_warning, DownloadStatus.backup_problem} 110DownloadFailure = {DownloadStatus.download_and_backup_failed, DownloadStatus.download_failed} 111 112 113download_status_error_severity = { 114 DownloadStatus.downloaded_with_warning: ErrorType.warning, 115 DownloadStatus.backup_problem: ErrorType.serious_error, 116 DownloadStatus.download_and_backup_failed: ErrorType.serious_error, 117 DownloadStatus.download_failed: ErrorType.serious_error 118} 119 120 121DownloadUpdateMilliseconds = 1000 122DownloadUpdateSeconds = DownloadUpdateMilliseconds / 1000 123# How many seconds to delay showing the time remaining and download speed 124ShowTimeAndSpeedDelay = 8.0 125 126 127class RightSideButton(IntEnum): 128 destination = 0 129 rename = 1 130 jobcode = 2 131 backup = 3 132 133 134class ThumbnailCacheStatus(Enum): 135 not_ready = 1 136 orientation_unknown = 2 137 ready = 3 138 fdo_256_ready = 4 139 generation_failed = 5 140 141 142class ThumbnailCacheDiskStatus(Enum): 143 found = 1 144 not_found = 2 145 failure = 3 146 unknown = 4 147 148 149class ThumbnailCacheOrigin(Enum): 150 thumbnail_cache = 1 151 fdo_cache = 2 152 153 154class DisplayingFilesOfType(Enum): 155 photos = 1 156 videos = 2 157 photos_and_videos = 3 158 159 160BackupLocationType = DisplayingFilesOfType 161BackupFailureType = DisplayingFilesOfType 162DownloadingFileTypes = DisplayingFilesOfType 163 164 165class DestinationDisplayType(Enum): 166 folder_only = 1 167 usage_only = 2 168 folders_and_usage = 3 169 170 171class ExifSource(Enum): 172 raw_bytes = 1 173 app1_segment = 2 174 actual_file = 3 175 176 177class DestinationDisplayMousePos(Enum): 178 normal = 1 179 menu = 2 180 181 182class DestinationDisplayTooltipState(Enum): 183 menu = 1 184 path = 2 185 storage_space = 3 186 187 188class DeviceType(Enum): 189 camera = 1 190 volume = 2 191 path = 3 192 193 194class BackupDeviceType: 195 volume = 1 196 path = 2 197 198 199class DeviceState(Enum): 200 pre_scan = 1 201 scanning = 2 202 idle = 3 203 thumbnailing = 4 204 downloading = 5 205 finished = 6 206 207 208class FileType(IntEnum): 209 photo = 1 210 video = 2 211 212 213class FileExtension(Enum): 214 raw = 1 215 jpeg = 2 216 heif = 3 217 other_photo = 4 218 video = 5 219 audio = 6 220 unknown = 7 221 222 223class FileSortPriority(IntEnum): 224 high = 1 225 low = 2 226 227 228class KnownDeviceType(IntEnum): 229 volume_whitelist = 1 230 volume_blacklist = 2 231 camera_blacklist = 3 232 233 234class RenameAndMoveStatus(Enum): 235 download_started = 1 236 download_completed = 2 237 238 239class BackupStatus(Enum): 240 backup_started = 1 241 backup_completed = 2 242 243 244class ThumbnailSize(IntEnum): 245 width = 160 246 height = 120 247 248 249class ApplicationState(Enum): 250 normal = 1 251 exiting = 2 252 253 254class Show(IntEnum): 255 all = 1 256 new_only = 2 257 258 259class Sort(IntEnum): 260 modification_time = 1 261 checked_state = 2 262 filename = 3 263 extension = 4 264 file_type = 5 265 device = 6 266 267 268class JobCodeSort(IntEnum): 269 last_used = 1 270 code = 2 271 272 273Checked_Status = { 274 Qt.Checked: 'checked', 275 Qt.Unchecked: 'unchecked', 276 Qt.PartiallyChecked: 'partially checked' 277} 278 279 280class Roles(IntEnum): 281 previously_downloaded = Qt.UserRole 282 extension = Qt.UserRole + 1 283 download_status = Qt.UserRole + 2 284 has_audio = Qt.UserRole + 3 285 secondary_attribute = Qt.UserRole + 4 286 path = Qt.UserRole + 5 287 uri = Qt.UserRole + 6 288 camera_memory_card = Qt.UserRole + 7 289 scan_id = Qt.UserRole + 8 290 device_details = Qt.UserRole + 9 291 storage = Qt.UserRole + 10 292 mtp = Qt.UserRole + 11 293 is_camera = Qt.UserRole + 12 294 sort_extension = Qt.UserRole + 13 295 filename = Qt.UserRole + 14 296 highlight = Qt.UserRole + 16 297 folder_preview = Qt.UserRole + 17 298 download_subfolder = Qt.UserRole + 18 299 device_type = Qt.UserRole + 19 300 download_statuses = Qt.UserRole + 20 301 job_code = Qt.UserRole + 21 302 uids = Qt.UserRole + 22 303 304 305class ExtractionTask(Enum): 306 undetermined = 1 307 bypass = 2 308 load_file_directly = 3 309 load_file_and_exif_directly = 4 310 load_file_directly_metadata_from_secondary = 5 311 load_from_bytes = 6 312 load_from_bytes_metadata_from_temp_extract = 7 313 load_from_exif = 8 314 extract_from_file = 9 315 extract_from_file_and_load_metadata = 10 316 load_from_exif_buffer = 11 317 load_heif_directly = 12 318 load_heif_and_exif_directly = 13 319 320 321class ExtractionProcessing(Enum): 322 resize = 1 323 orient = 2 324 strip_bars_photo = 3 325 strip_bars_video = 4 326 add_film_strip = 5 327 328 329# Approach device uses to store timestamps 330# i.e. whether assumes are located in utc timezone or local 331class DeviceTimestampTZ(Enum): 332 undetermined = 1 333 unknown = 2 334 is_utc = 3 335 is_local = 4 336 337 338class CameraErrorCode(Enum): 339 inaccessible = 1 340 locked = 2 341 read = 3 342 write = 4 343 344 345class ViewRowType(Enum): 346 header = 1 347 content = 2 348 349 350class Align(Enum): 351 top = 1 352 bottom = 2 353 354 355class NameGenerationType(Enum): 356 photo_name = 1 357 video_name = 2 358 photo_subfolder = 3 359 video_subfolder = 4 360 361 362class CustomColors(Enum): 363 color1 = '#7a9c38' # green 364 color2 = '#cb493f' # red 365 color3 = '#d17109' # orange 366 color4 = '#4D8CDC' # blue 367 color5 = '#5f6bfe' # purple 368 color6 = '#6d7e90' # greyish 369 color7 = '#ffff00' # bright yellow 370 371 372PaleGray = '#d7d6d5' 373DarkGray = '#35322f' 374MediumGray = '#5d5b59' 375DoubleDarkGray = '#1e1b18' 376 377 378ExtensionColorDict = { 379 FileExtension.raw: CustomColors.color1, 380 FileExtension.video: CustomColors.color2, 381 FileExtension.jpeg: CustomColors.color4, 382 FileExtension.heif: CustomColors.color5, 383 FileExtension.other_photo: CustomColors.color5 384} 385 386 387def extensionColor(ext_type: FileExtension) -> QColor: 388 try: 389 return QColor(ExtensionColorDict[ext_type].value) 390 except KeyError: 391 return QColor(0, 0, 0) 392 393 394FileTypeColorDict = { 395 FileType.photo: CustomColors.color1, 396 FileType.video: CustomColors.color2 397} 398 399 400def fileTypeColor(file_type: FileType) -> QColor: 401 try: 402 return QColor(FileTypeColorDict[file_type].value) 403 except KeyError: 404 return QColor(CustomColors.color3.value) 405 406 407# Position of preference values in file renaming and subfolder generation editor: 408class PrefPosition(Enum): 409 on_left = 1 410 at = 2 411 on_left_and_at = 3 412 positioned_in = 4 413 not_here = 5 414 415 416# Values in minutes: 417proximity_time_steps = [5, 10, 15, 30, 45, 60, 90, 120, 180, 240, 480, 960, 1440] 418 419 420class TemporalProximityState(Enum): 421 empty = 1 422 pending = 2 # e.g. 2 devices scanning, only 1 scan finished 423 generating = 3 424 regenerate = 4 425 generated = 5 426 ctime_rebuild = 6 427 ctime_rebuild_proceed = 7 428 429 430class StandardFileLocations(Enum): 431 home = 1 432 desktop = 2 433 file_system = 3 434 documents = 4 435 music = 5 436 pictures = 6 437 videos = 7 438 downloads = 8 439 440 441 442max_remembered_destinations = 10 443 444ThumbnailBackgroundName = MediumGray 445EmptyViewHeight = 20 446 447DeviceDisplayPadding = 6 448DeviceShadingIntensity = 104 449 450# How many steps with which to highlight thumbnail cells 451FadeSteps = 20 452FadeMilliseconds = 700 453 454 455# horizontal and vertical margin for thumbnail rectangles 456thumbnail_margin = 10 457 458 459def minPanelWidth() -> int: 460 """ 461 Minimum width of panels on left and right side of main window. 462 463 Derived from standard font size. 464 465 :return: size in pixels 466 """ 467 468 return int(QFontMetrics(QFont()).height() * 13.5) 469 470 471def minFileSystemViewHeight() -> int: 472 """ 473 Minimum height of file system views on left and right side of main window. 474 475 Derived from standard font size. 476 477 :return: size in pixels 478 """ 479 480 return QFontMetrics(QFont()).height() * 7 481 482 483def minGridColumnWidth() -> int: 484 return int(QFontMetrics(QFont()).height() * 1.3333333333333333) 485 486 487def standardProgressBarWidth() -> int: 488 return int(QFontMetrics(QFont()).height() * 20) 489 490 491# Be sure to update gvfs_controls_mounts() if updating this 492class Desktop(Enum): 493 gnome = 1 494 unity = 2 495 cinnamon = 3 496 kde = 4 497 xfce = 5 498 mate = 6 499 lxde = 7 500 lxqt = 8 501 ubuntugnome = 9 502 popgnome = 10 503 deepin = 11 504 zorin = 12 505 ukui = 13 506 pantheon = 14 507 unknown = 15 508 509 510class FileManagerType(Enum): 511 regular = 1 512 select = 2 513 dir_only_uri = 3 514 show_item = 4 515 show_items = 5 516 517 518FileManagerBehavior = dict( 519 nautilus=FileManagerType.select, 520 dolphin=FileManagerType.select, 521 caja=FileManagerType.dir_only_uri, 522 thunar=FileManagerType.dir_only_uri, 523 nemo=FileManagerType.regular, 524 pcmanfm=FileManagerType.dir_only_uri, 525 peony=FileManagerType.show_items, 526) 527FileManagerBehavior['pcmanfm-qt'] = FileManagerType.dir_only_uri 528FileManagerBehavior['dde-file-manager'] = FileManagerType.show_item 529FileManagerBehavior['io.elementary.files'] = FileManagerType.regular 530 531 532DefaultFileBrowserFallback = dict( 533 gnome='nautilus', 534 ubuntugnome='nautilus', 535 popgnome='nautilus', 536 unity='nautilus', 537 kde='dolphin', 538 cinnamon='nemo', 539 mate='caja', 540 xfce='thunar', 541 lxde='pcmanfm', 542 lxqt='pcmanfm-qt', 543 deepin='dde-file-manager', 544 kylin='peony', 545 pantheon='io.elementary.files', 546) 547 548 549# Sync with value in install.py 550class Distro(Enum): 551 debian = 1 552 ubuntu = 2 553 fedora = 3 554 neon = 4 555 linuxmint = 5 556 zorin = 6 557 arch = 7 558 opensuse = 8 559 manjaro = 9 560 galliumos = 10 561 peppermint = 11 562 elementary = 13 563 centos = 14 564 centos7 = 15 565 gentoo = 16 566 deepin = 17 567 kylin = 18 568 popos = 19 569 unknown = 20 570 571 572orientation_offset = dict( 573 arw=106, 574 cr2=126, 575 cr3=60000, # assuming ExifTool (exiv2 >= 0.28 required for CR3) 576 dcr=7684, 577 dng=144, 578 mef=144, 579 mrw=152580, 580 nef=144, 581 nrw=94, 582 orf=132, 583 pef=118, 584 raf=208, 585 raw=742404, 586 rw2=1004548, 587 sr2=82, 588 srw=46 589) 590orientation_offset['3fr'] = 132 591 592orientation_offset_exiftool = dict( 593 arw=350, 594 cr2=320, 595 cr3=60000, 596 crw=20, 597 dcr=8196, 598 dng=644, 599 iiq=20, 600 mef=376, 601 mrw=152580, 602 nef=392, 603 nrw=94, 604 orf=6148, 605 pef=332, 606 raf=70660, 607 raw=548, 608 rw2=709636, 609 sr2=276, 610 srw=126 611) 612orientation_offset_exiftool['3fr'] = 376 613 614datetime_offset = dict( 615 arw=1540, 616 cr2=1028, 617 cr3=60000, # assuming ExifTool 618 dng=119812, 619 mef=772, 620 mrw=152580, 621 nef=14340, 622 nrw=1540, 623 orf=6660, 624 pef=836, 625 raf=1796, 626 raw=964, 627 rw2=3844, 628 sr2=836, 629 srw=508, 630 mts=5000, 631 m2t=5000, 632 m2ts=5000, 633 mp4=50000, 634 avi=50000, 635 mov=250000, 636) 637datetime_offset['3fr'] = 1540 638datetime_offset['3gp'] = 5000 639 640datetime_offset_exiftool = dict( 641 arw=1540, 642 cr2=1000, # varies widely :-/ 643 cr3=60000, 644 crw=20, 645 dng=3000, # varies widely :-/ 646 mef=772, 647 mrw=152580, 648 nef=13316, 649 nrw=488, 650 orf=7172, 651 pef=836, 652 raf=70660, 653 raw=932, 654 rw2=709636, 655 sr2=836, 656 srw=496, 657 x3f=69220070, 658 mts=5000, 659 m2t=5000, 660 m2ts=5000, 661 mp4=50000, 662 avi=50000, 663 mov=250000, 664) 665datetime_offset_exiftool['3fr'] = 1042 666datetime_offset_exiftool['3gp'] = 5000 667 668all_tags_offset = dict( 669 arw=1848, 670 cr2=94622, 671 cr3=60000, # assuming ExifTool 672 dng=143774, 673 mef=965, 674 mrw=183096, 675 nef=1126814, 676 nrw=1848, 677 orf=812242, 678 pef=1042, 679 raf=13522, 680 raw=890885, 681 rw2=1205458, 682 sr2=1080, 683 srw=614, 684) 685all_tags_offset['3fr'] = 1848 686 687all_tags_offset_exiftool = dict( 688 arw=1540, 689 cr2=104453, 690 cr3=60000, 691 dng=143774, 692 dcr=10450, 693 mef=965, 694 mrw=183096, 695 nef=77213623, 696 nrw=1848, 697 orf=29113613, 698 pef=183096, 699 raf=84792, 700 raw=890885, 701 rw2=1205458, 702 sr2=1080, 703 srw=222418, 704 x3f=7380128, 705 mp4=130000, 706 mts=1300000, 707 mt2=1300000, 708 m2ts=1300000, 709 avi=50000, 710 mov=250000 711) 712all_tags_offset_exiftool['3fr'] = 1042 713 714thumbnail_offset = dict( 715 jpg=100000, 716 jpeg=100000, 717 dng=100000, 718 avi=500000, 719 mod=500000, 720 mov=2000000, 721 mp4=2000000, 722 mts=600000, 723 m2t=600000, 724 mpg=500000, 725 mpeg=500000, 726 tod=500000, 727) 728 729# Repeat video information here 730thumbnail_offset_exiftool = dict( 731 cr2=694277, 732 cr3=45470, 733 mrw=84792, 734 nef=77213623, 735 nrw=45470, 736 raf=84792, 737 raw=890885, 738 rw2=1205458, 739 sr2=222418, 740 srw=812242, 741 avi=500000, 742 mod=500000, 743 mov=2000000, 744 mp4=2000000, 745 mts=600000, 746 m2t=600000, 747 mpg=500000, 748 mpeg=500000, 749 tod=500000, 750) 751 752 753 754class RememberThisMessage(Enum): 755 remember_choice = 1 756 do_not_ask_again = 2 757 do_not_warn_again = 3 758 do_not_warn_again_about_missing_libraries = 4 759 760 761class RememberThisButtons(Enum): 762 yes_no = 1 763 ok = 2 764 765 766class CompletedDownloads(IntEnum): 767 keep = 1 768 clear = 2 769 prompt = 3 770 771 772class TreatRawJpeg(IntEnum): 773 one_photo = 1 774 two_photos = 2 775 776 777class MarkRawJpeg(IntEnum): 778 no_jpeg = 1 779 no_raw = 2 780 both = 3 781 782 783# see https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals 784class Plural(Enum): 785 zero = 1 786 two_form_single = 2 787 two_form_plural = 3 788 789 790class ScalingAction(Enum): 791 turned_on = 1 792 not_set = 2 793 already_set = 3 794 795 796class ScalingDetected(Enum): 797 Qt = 1 798 Xsetting = 2 799 Qt_and_Xsetting = 3 800 undetected = 4 801 802 803# Use the character . to for download_name and path to indicate the user manually marked a 804# file as previously downloaded 805manually_marked_previously_downloaded = '.' 806 807