2054a0d0de6b22dd2f7bafc52a846e16550a8922
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 Make it so that ``import cloudfiles`` does not pick THIS file, but the
19 python-cloudfiles one.
21 http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports
23 from __future__
import absolute_import
25 from mediagoblin
.storage
import StorageInterface
, clean_listy_filepath
31 class CloudFilesStorage(StorageInterface
):
33 OpenStack/Rackspace Cloud's Swift/CloudFiles support
38 def __init__(self
, **kwargs
):
39 self
.param_container
= kwargs
.get('cloudfiles_container')
40 self
.param_user
= kwargs
.get('cloudfiles_user')
41 self
.param_api_key
= kwargs
.get('cloudfiles_api_key')
42 self
.param_host
= kwargs
.get('cloudfiles_host')
43 self
.param_use_servicenet
= kwargs
.get('cloudfiles_use_servicenet')
45 # the Mime Type webm doesn't exists, let's add it
46 mimetypes
.add_type("video/webm", "webm")
48 if not self
.param_host
:
49 print('No CloudFiles host URL specified, '
50 'defaulting to Rackspace US')
52 self
.connection
= cloudfiles
.get_connection(
53 username
=self
.param_user
,
54 api_key
=self
.param_api_key
,
55 servicenet
=True if self
.param_use_servicenet
== 'true' or \
56 self
.param_use_servicenet
== True else False)
58 if not self
.param_container
== \
59 self
.connection
.get_container(self
.param_container
):
60 self
.container
= self
.connection
.create_container(
62 self
.container
.make_public(
65 self
.container
= self
.connection
.get_container(
68 self
.container_uri
= self
.container
.public_uri()
70 def _resolve_filepath(self
, filepath
):
72 clean_listy_filepath(filepath
))
74 def file_exists(self
, filepath
):
76 object = self
.container
.get_object(
77 self
._resolve
_filepath
(filepath
))
79 except cloudfiles
.errors
.NoSuchObject
:
82 def get_file(self
, filepath
, *args
, **kwargs
):
84 - Doesn't care about the "mode" argument
87 obj
= self
.container
.get_object(
88 self
._resolve
_filepath
(filepath
))
89 except cloudfiles
.errors
.NoSuchObject
:
90 obj
= self
.container
.create_object(
91 self
._resolve
_filepath
(filepath
))
93 mimetype
= mimetypes
.guess_type(
97 obj
.content_type
= mimetype
[0]
98 # this should finally fix the bug #429
99 meta_data
= {'mime-type' : mimetype
}
100 obj
.metadata
= meta_data
102 return CloudFilesStorageObjectWrapper(obj
, *args
, **kwargs
)
104 def delete_file(self
, filepath
):
105 # TODO: Also delete unused directories if empty (safely, with
106 # checks to avoid race conditions).
108 self
.container
.delete_object(
109 self
._resolve
_filepath
(filepath
))
110 except cloudfiles
.container
.ResponseError
:
116 def file_url(self
, filepath
):
119 self
._resolve
_filepath
(filepath
)])
122 class CloudFilesStorageObjectWrapper():
124 Wrapper for python-cloudfiles's cloudfiles.storage_object.Object
125 used to circumvent the mystic `medium.jpg` corruption issue, where
126 we had both python-cloudfiles and PIL doing buffering on both
127 ends and that breaking things.
129 This wrapper currently meets mediagoblin's needs for a public_store
132 def __init__(self
, storage_object
, *args
, **kwargs
):
133 self
.storage_object
= storage_object
135 def read(self
, *args
, **kwargs
):
136 return self
.storage_object
.read(*args
, **kwargs
)
138 def write(self
, data
, *args
, **kwargs
):
140 write data to the cloudfiles storage object
142 The original motivation for this wrapper is to ensure
143 that buffered writing to a cloudfiles storage object does not overwrite
144 any preexisting data.
146 Currently this method does not support any write modes except "append".
147 However if we should need it it would be easy implement.
149 if self
.storage_object
.size
and type(data
) == str:
150 data
= self
.read() + data
152 self
.storage_object
.write(data
, *args
, **kwargs
)
159 Context Manager API implementation
160 http://docs.python.org/library/stdtypes.html#context-manager-types
164 def __exit__(self
, *exc_info
):
166 Context Manger API implementation