@@ -0,0 +1,19 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+from __future__ import unicode_literals |
|
3 |
+ |
|
4 |
+from django.db import models, migrations |
|
5 |
+ |
|
6 |
+ |
|
7 |
+class Migration(migrations.Migration): |
|
8 |
+ |
|
9 |
+ dependencies = [ |
|
10 |
+ ('account', '0003_auto_20151202_2313'), |
|
11 |
+ ] |
|
12 |
+ |
|
13 |
+ operations = [ |
|
14 |
+ migrations.AlterField( |
|
15 |
+ model_name='lensmaninfo', |
|
16 |
+ name='lensman_id', |
|
17 |
+ field=models.CharField(null=True, max_length=255, blank=True, help_text='\u6444\u5f71\u5e08\u552f\u4e00\u6807\u8bc6', unique=True, verbose_name='lensman_id', db_index=True), |
|
18 |
+ ), |
|
19 |
+ ] |
@@ -150,6 +150,9 @@ REST_FRAMEWORK = { |
||
150 | 150 |
# 唯一标识设置 |
151 | 151 |
CURTAIL_UUID_LENGTH = 7 |
152 | 152 |
|
153 |
+# 水印设置 |
|
154 |
+WATERMARK_LOGO = os.path.join(PROJ_DIR, 'static/pai2/img/paiai_96_96.png').replace('\\', '/') |
|
155 |
+ |
|
153 | 156 |
# 域名设置 |
154 | 157 |
DOMAIN = 'http://xfoto.com.cn' |
155 | 158 |
|
@@ -11,7 +11,7 @@ class UUIDInfoAdmin(admin.ModelAdmin): |
||
11 | 11 |
|
12 | 12 |
|
13 | 13 |
class PhotosInfoAdmin(admin.ModelAdmin): |
14 |
- list_display = ('lensman_id', 'session_id', 'photo_id', 'photo_name', 'photo_path', 'status', 'created_at', 'updated_at') |
|
14 |
+ list_display = ('lensman_id', 'session_id', 'photo_id', 'p_photo_path', 'status', 'created_at', 'updated_at') |
|
15 | 15 |
list_filter = ('lensman_id', 'status') |
16 | 16 |
|
17 | 17 |
|
@@ -0,0 +1,42 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+from __future__ import unicode_literals |
|
3 |
+ |
|
4 |
+from django.db import models, migrations |
|
5 |
+ |
|
6 |
+ |
|
7 |
+class Migration(migrations.Migration): |
|
8 |
+ |
|
9 |
+ dependencies = [ |
|
10 |
+ ('photo', '0004_photosinfo_photo_name'), |
|
11 |
+ ] |
|
12 |
+ |
|
13 |
+ operations = [ |
|
14 |
+ migrations.RemoveField( |
|
15 |
+ model_name='photosinfo', |
|
16 |
+ name='photo_name', |
|
17 |
+ ), |
|
18 |
+ migrations.RemoveField( |
|
19 |
+ model_name='photosinfo', |
|
20 |
+ name='photo_path', |
|
21 |
+ ), |
|
22 |
+ migrations.AddField( |
|
23 |
+ model_name='photosinfo', |
|
24 |
+ name='l_photo_path', |
|
25 |
+ field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='l_photo_path', blank=True), |
|
26 |
+ ), |
|
27 |
+ migrations.AddField( |
|
28 |
+ model_name='photosinfo', |
|
29 |
+ name='m_photo_path', |
|
30 |
+ field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='m_photo_path', blank=True), |
|
31 |
+ ), |
|
32 |
+ migrations.AddField( |
|
33 |
+ model_name='photosinfo', |
|
34 |
+ name='p_photo_path', |
|
35 |
+ field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='p_photo_path', blank=True), |
|
36 |
+ ), |
|
37 |
+ migrations.AddField( |
|
38 |
+ model_name='photosinfo', |
|
39 |
+ name='r_photo_path', |
|
40 |
+ field=models.CharField(help_text='\u7167\u7247\u5b58\u653e\u8def\u5f84', max_length=255, null=True, verbose_name='r_photo_path', blank=True), |
|
41 |
+ ), |
|
42 |
+ ] |
@@ -32,8 +32,10 @@ class PhotosInfo(CreateUpdateMixin): |
||
32 | 32 |
lensman_id = models.CharField(_(u'lensman_id'), max_length=255, blank=True, null=True, help_text=u'摄影师唯一标识', db_index=True) |
33 | 33 |
session_id = models.CharField(_(u'session_id'), max_length=255, blank=True, null=True, help_text=u'照片组唯一标识', db_index=True) |
34 | 34 |
photo_id = models.CharField(_(u'photo_id'), max_length=255, blank=True, null=True, help_text=u'照片唯一标识', db_index=True, unique=True) |
35 |
- photo_name = models.CharField(_(u'photo_name'), max_length=255, blank=True, null=True, help_text=u'照片存放名称') |
|
36 |
- photo_path = models.CharField(_(u'photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径') |
|
35 |
+ p_photo_path = models.CharField(_(u'p_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径') |
|
36 |
+ m_photo_path = models.CharField(_(u'm_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径') |
|
37 |
+ l_photo_path = models.CharField(_(u'l_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径') |
|
38 |
+ r_photo_path = models.CharField(_(u'r_photo_path'), max_length=255, blank=True, null=True, help_text=u'照片存放路径') |
|
37 | 39 |
|
38 | 40 |
class Meta: |
39 | 41 |
verbose_name = _('photosinfo') |
@@ -46,9 +48,20 @@ class PhotosInfo(CreateUpdateMixin): |
||
46 | 48 |
return u'{0.pk}'.format(self) |
47 | 49 |
|
48 | 50 |
@property |
49 |
- def photo_url(self): |
|
50 |
- return u'{0}/media/{1}'.format(settings.DOMAIN, self.photo_path) if self.photo_path else '' |
|
51 |
- # return u'{0}/p/{1}'.format(settings.DOMAIN, self.photo_name) if self.photo_name else '' |
|
51 |
+ def p_photo_url(self): |
|
52 |
+ return u'{0}/media/{1}'.format(settings.DOMAIN, self.p_photo_path) if self.p_photo_path else '' |
|
53 |
+ |
|
54 |
+ @property |
|
55 |
+ def m_photo_url(self): |
|
56 |
+ return u'{0}/media/{1}'.format(settings.DOMAIN, self.m_photo_path) if self.m_photo_path else '' |
|
57 |
+ |
|
58 |
+ @property |
|
59 |
+ def l_photo_url(self): |
|
60 |
+ return u'{0}/media/{1}'.format(settings.DOMAIN, self.l_photo_path) if self.l_photo_path else '' |
|
61 |
+ |
|
62 |
+ @property |
|
63 |
+ def r_photo_url(self): |
|
64 |
+ return u'{0}/media/{1}'.format(settings.DOMAIN, self.r_photo_path) if self.r_photo_path else '' |
|
52 | 65 |
|
53 | 66 |
def _data(self): |
54 | 67 |
return { |
@@ -56,7 +69,6 @@ class PhotosInfo(CreateUpdateMixin): |
||
56 | 69 |
'user': self.lensman_id, |
57 | 70 |
'session': self.session_id, |
58 | 71 |
'photo': self.photo_id, |
59 |
- # 'photo_url': self.photo_url, |
|
60 | 72 |
} |
61 | 73 |
|
62 | 74 |
data = property(_data) |
@@ -6,7 +6,7 @@ |
||
6 | 6 |
</head> |
7 | 7 |
<body> |
8 | 8 |
{% for photo in photos %} |
9 |
- <div><img src="{{ photo.photo_url }}"></div> |
|
9 |
+ <div><img src="{{ photo.p_photo_url }}"></div> |
|
10 | 10 |
{% endfor %} |
11 | 11 |
</body> |
12 | 12 |
</html> |
@@ -1,5 +1,6 @@ |
||
1 | 1 |
# -*- coding: utf-8 -*- |
2 | 2 |
|
3 |
+from django.conf import settings |
|
3 | 4 |
from django.core.files.storage import default_storage |
4 | 5 |
from django.db import transaction |
5 | 6 |
from django.http import JsonResponse |
@@ -12,6 +13,7 @@ from photo.models import UUIDInfo, PhotosInfo |
||
12 | 13 |
from photo.serializers import PhotosInfoSerializer |
13 | 14 |
|
14 | 15 |
from utils.uuid_utils import curtailUUID |
16 |
+from utils.watermark_utils import watermark_wrap |
|
15 | 17 |
|
16 | 18 |
import os |
17 | 19 |
|
@@ -83,20 +85,25 @@ def upload_photo(request): |
||
83 | 85 |
photo_id = curtailUUID(PhotosInfo, 'photo_id') |
84 | 86 |
|
85 | 87 |
_, extension = os.path.splitext(photo.name) |
86 |
- # photo_path = 'photo/{0}/{1}/{2}{3}'.format(lensman_id, session_id, photo_id, extension) |
|
87 |
- photo_name = '{0}{1}'.format(photo_id, extension) |
|
88 |
- photo_path = 'photo/{0}'.format(photo_name) |
|
88 |
+ m_photo_path = 'photo/{photo_id}_m{extension}'.format(photo_id=photo_id, extension=extension) |
|
89 | 89 |
|
90 |
- if default_storage.exists(photo_path): |
|
91 |
- default_storage.delete(photo_path) |
|
92 |
- default_storage.save(photo_path, photo) |
|
90 |
+ if default_storage.exists(m_photo_path): |
|
91 |
+ default_storage.delete(m_photo_path) |
|
92 |
+ default_storage.save(m_photo_path, photo) |
|
93 |
+ |
|
94 |
+ p_photo_path = 'photo/{photo_id}_p{extension}'.format(photo_id=photo_id, extension=extension) |
|
95 |
+ watermark_wrap( |
|
96 |
+ os.path.join(settings.MEDIA_ROOT, m_photo_path).replace('\\', '/'), |
|
97 |
+ settings.WATERMARK_LOGO, |
|
98 |
+ os.path.join(settings.MEDIA_ROOT, p_photo_path).replace('\\', '/') |
|
99 |
+ ) |
|
93 | 100 |
|
94 | 101 |
photo, created = PhotosInfo.objects.get_or_create( |
95 | 102 |
lensman_id=lensman_id, |
96 | 103 |
session_id=session_id, |
97 | 104 |
photo_id=photo_id, |
98 |
- photo_name=photo_name, |
|
99 |
- photo_path=photo_path |
|
105 |
+ p_photo_path=p_photo_path, |
|
106 |
+ m_photo_path=m_photo_path, |
|
100 | 107 |
) |
101 | 108 |
|
102 | 109 |
return JsonResponse({ |
@@ -113,22 +120,22 @@ def session_detail(request, session): |
||
113 | 120 |
|
114 | 121 |
def photo_standard(request, photo): |
115 | 122 |
photo = PhotosInfo.objects.get(photo_id=photo) |
116 |
- return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url}) |
|
123 |
+ return render(request, 'photo/photo_detail.html', {'photo_url': photo.p_photo_url}) |
|
117 | 124 |
|
118 | 125 |
|
119 | 126 |
def photo_medium(request, photo): |
120 | 127 |
photo = PhotosInfo.objects.get(photo_id=photo) |
121 |
- return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url}) |
|
128 |
+ return render(request, 'photo/photo_detail.html', {'photo_url': photo.m_photo_url}) |
|
122 | 129 |
|
123 | 130 |
|
124 | 131 |
def photo_large(request, photo): |
125 | 132 |
photo = PhotosInfo.objects.get(photo_id=photo) |
126 |
- return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url}) |
|
133 |
+ return render(request, 'photo/photo_detail.html', {'photo_url': photo.l_photo_url}) |
|
127 | 134 |
|
128 | 135 |
|
129 | 136 |
def photo_raw(request, photo): |
130 | 137 |
photo = PhotosInfo.objects.get(photo_id=photo) |
131 |
- return render(request, 'photo/photo_detail.html', {'photo_url': photo.photo_url}) |
|
138 |
+ return render(request, 'photo/photo_detail.html', {'photo_url': photo.r_photo_url}) |
|
132 | 139 |
|
133 | 140 |
|
134 | 141 |
class PhotoInfoViewSet(viewsets.ModelViewSet): |
@@ -0,0 +1,62 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+ |
|
3 |
+try: |
|
4 |
+ import Image |
|
5 |
+ import ImageEnhance |
|
6 |
+except ImportError: |
|
7 |
+ from PIL import Image, ImageEnhance |
|
8 |
+ |
|
9 |
+ |
|
10 |
+def reduce_opacity(im, opacity): |
|
11 |
+ """Returns an image with reduced opacity.""" |
|
12 |
+ assert 0 <= opacity <= 1 |
|
13 |
+ im = im.convert('RGBA') if im.mode != 'RGBA' else im.copy() |
|
14 |
+ alpha = im.split()[3] |
|
15 |
+ alpha = ImageEnhance.Brightness(alpha).enhance(opacity) |
|
16 |
+ im.putalpha(alpha) |
|
17 |
+ return im |
|
18 |
+ |
|
19 |
+ |
|
20 |
+def watermark(im, mark, position, opacity=1): |
|
21 |
+ """Adds a watermark to an image.""" |
|
22 |
+ if opacity < 1: |
|
23 |
+ mark = reduce_opacity(mark, opacity) |
|
24 |
+ if im.mode != 'RGBA': |
|
25 |
+ im = im.convert('RGBA') |
|
26 |
+ # create a transparent layer the size of the image and draw the |
|
27 |
+ # watermark in that layer. |
|
28 |
+ layer = Image.new('RGBA', im.size, (0, 0, 0, 0)) |
|
29 |
+ if position == 'tile': |
|
30 |
+ for y in range(0, im.size[1], mark.size[1]): |
|
31 |
+ for x in range(0, im.size[0], mark.size[0]): |
|
32 |
+ layer.paste(mark, (x, y)) |
|
33 |
+ elif position == 'scale': |
|
34 |
+ # scale, but preserve the aspect ratio |
|
35 |
+ ratio = min( |
|
36 |
+ float(im.size[0]) / mark.size[0], float(im.size[1]) / mark.size[1]) |
|
37 |
+ w = int(mark.size[0] * ratio) |
|
38 |
+ h = int(mark.size[1] * ratio) |
|
39 |
+ mark = mark.resize((w, h)) |
|
40 |
+ layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2)) |
|
41 |
+ else: |
|
42 |
+ layer.paste(mark, position) |
|
43 |
+ # composite the watermark with the layer |
|
44 |
+ return Image.composite(layer, im, layer) |
|
45 |
+ |
|
46 |
+ |
|
47 |
+def watermark_wrap(im_path, mark_path, save_path=''): |
|
48 |
+ im, mark = Image.open(im_path), Image.open(mark_path) |
|
49 |
+ new_im = watermark(im, mark, (50, 50), 0.5) |
|
50 |
+ new_im.save(save_path or im_path) |
|
51 |
+ |
|
52 |
+ |
|
53 |
+def watermark_test(): |
|
54 |
+ im, mark = Image.open('original_CGzC_10a50000c8811190.jpg'), Image.open('paiai_96_96.png') |
|
55 |
+ watermark(im, mark, 'tile', 0.5).show() |
|
56 |
+ watermark(im, mark, 'scale', 1.0).show() |
|
57 |
+ watermark(im, mark, (50, 50), 0.5).show() |
|
58 |
+ |
|
59 |
+ |
|
60 |
+if __name__ == '__main__': |
|
61 |
+ # watermark_test() |
|
62 |
+ watermark_wrap('original_CGzC_10a50000c8811190.jpg', 'paiai_96_96.png') |