Website to allow artists to upload their artwork to multiple sites from a single interface https://multiupload.us/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
4.7KB

  1. from enum import Enum
  2. from io import BytesIO
  3. from os.path import join
  4. from typing import List, Tuple
  5. from flask import current_app
  6. from PIL import Image
  7. from raven import breadcrumbs
  8. from constant import Sites
  9. from description import parse_description
  10. def is_hashtag(tag: str) -> bool:
  11. """Returns if a tag is a hashtag."""
  12. return tag.startswith('#')
  13. class Rating(Enum):
  14. """Rating is the rating for a submission."""
  15. general = 'general'
  16. mature = 'mature'
  17. explicit = 'explicit'
  18. class Submission(object):
  19. """Submission is a normalized representation of something to post."""
  20. title: str = None # Title of submission
  21. description: str = None # Description of submission
  22. tags: List[str] = None # Tags of submission
  23. hashtags: List[str] = None # Hashtags of submission (only for Twitter)
  24. rating: Rating = None # Rating of submission
  25. image_filename: str = None # Filename of submission
  26. image_bytes: BytesIO = None # Bytes of image in submission
  27. image_mimetype: str = None # Mime type of image in submission
  28. _image_size: int = None
  29. def __init__(self, title: str, description: str, tags: str, rating: str, image):
  30. """Create a new Submission automatically parsing tags into regular tags
  31. and hashtags, and reading the image in."""
  32. self.title = title
  33. self.description = description
  34. self.rating = Rating[rating]
  35. parsed_tags = Submission.tags_from_str(tags)
  36. self.tags, self.hashtags = parsed_tags
  37. if hasattr(image, 'original_filename'):
  38. if image.original_filename:
  39. self.image_filename = image.original_filename
  40. self.image_mimetype = image.image_mimetype
  41. with open(
  42. join(current_app.config['UPLOAD_FOLDER'], image.image_filename),
  43. 'rb',
  44. ) as f:
  45. self.image_bytes = BytesIO(f.read())
  46. else:
  47. self.image_filename = image.filename
  48. self.image_bytes = BytesIO(image.read())
  49. self.image_mimetype = image.mimetype
  50. if self.image_bytes:
  51. self.image_bytes.seek(0)
  52. def get_image(self) -> Tuple[str, BytesIO]:
  53. """Returns a tuple suitable for uploading."""
  54. self.image_bytes.seek(0)
  55. return self.image_filename, self.image_bytes
  56. def image_res(self) -> Tuple[int, int]:
  57. image = Image.open(self.image_bytes)
  58. height, width = image.height, image.width
  59. self.image_bytes.seek(0)
  60. return height, width
  61. def resize_image(
  62. self, height: int, width: int, replace: bool = False
  63. ) -> Tuple[str, BytesIO]:
  64. """Resize image to specified height and width with antialiasing"""
  65. image = Image.open(self.image_bytes)
  66. if image.height <= height and image.width <= width:
  67. self.image_bytes.seek(0)
  68. return self.image_filename, self.image_bytes
  69. # need to get extension from name due to bytes not having a name
  70. # when resizing this causes an issue as it cannot determine type
  71. if image.format:
  72. f = image.format
  73. else:
  74. f = None
  75. if not image.mode.startswith('RGB'):
  76. image = image.convert('RGBA') # Everything works better as RGB
  77. image.thumbnail((height, width), Image.ANTIALIAS)
  78. breadcrumbs.record(message=f'Attempting to resize image with extension {f}', category='furryapp', level='info')
  79. resized_image = BytesIO()
  80. image.save(resized_image, f)
  81. resized_image.seek(0)
  82. breadcrumbs.record(message='Resized image', category='furryapp', level='info')
  83. if replace:
  84. self.image_bytes = resized_image
  85. self.image_bytes.seek(0)
  86. return self.image_filename, resized_image
  87. def description_for_site(self, site: Sites) -> str:
  88. """Returns a formatted description for a specific site."""
  89. return parse_description(self.description, site.value)
  90. @property
  91. def image_size(self) -> int:
  92. if self._image_size:
  93. return self._image_size
  94. self._image_size = len(self.image_bytes.getbuffer())
  95. self.image_bytes.seek(0)
  96. return self._image_size
  97. @staticmethod
  98. def tags_from_str(tags: str) -> Tuple[List[str], List[str]]:
  99. """Takes in a string, and returns regular keywords and hashtags."""
  100. if ',' in tags:
  101. tag_list = tags.split(',')
  102. else:
  103. tag_list = tags.split(' ')
  104. tag_list = [tag.strip() for tag in filter(None, tag_list)]
  105. hashtags = []
  106. for keyword in tag_list:
  107. if keyword.startswith('#'):
  108. hashtags.append(keyword)
  109. tags_reg = list(filter(lambda x: not is_hashtag(x), tag_list))
  110. return tags_reg, hashtags