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.

submission.py 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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