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/>.
20 from mediagoblin
.db
.util
import atomic_update
21 from mediagoblin
import mg_globals
as mgg
23 from mediagoblin
.tools
.translate
import lazy_pass_to_ugettext
as _
25 _log
= logging
.getLogger(__name__
)
28 MEDIUM_SIZE
= 640, 640
31 def create_pub_filepath(entry
, filename
):
32 return mgg
.public_store
.get_unique_filepath(
37 class FilenameBuilder(object):
38 """Easily slice and dice filenames.
40 Initialize this class with an original file path, then use the fill()
41 method to create new filenames based on the original.
44 MAX_FILENAME_LENGTH
= 255 # VFAT's maximum filename length
46 def __init__(self
, path
):
47 """Initialize a builder from an original file path."""
48 self
.dirpath
, self
.basename
= os
.path
.split(path
)
49 self
.basename
, self
.ext
= os
.path
.splitext(self
.basename
)
50 self
.ext
= self
.ext
.lower()
52 def fill(self
, fmtstr
):
53 """Build a new filename based on the original.
55 The fmtstr argument can include the following:
56 {basename} -- the original basename, with the extension removed
57 {ext} -- the original extension, always lowercase
59 If necessary, {basename} will be truncated so the filename does not
60 exceed this class' MAX_FILENAME_LENGTH in length.
63 basename_len
= (self
.MAX_FILENAME_LENGTH
-
64 len(fmtstr
.format(basename
='', ext
=self
.ext
)))
65 return fmtstr
.format(basename
=self
.basename
[:basename_len
],
69 def mark_entry_failed(entry_id
, exc
):
71 Mark a media entry as having failed in its conversion.
73 Uses the exception that was raised to mark more information. If
74 the exception is a derivative of BaseProcessingFail then we can
75 store extra information that can be useful for users telling them
76 why their media failed to process.
79 - entry_id: The id of the media entry
82 # Was this a BaseProcessingFail? In other words, was this a
83 # type of error that we know how to handle?
84 if isinstance(exc
, BaseProcessingFail
):
85 # Looks like yes, so record information about that failure and any
86 # metadata the user might have supplied.
87 atomic_update(mgg
.database
.MediaEntry
,
90 u
'fail_error': exc
.exception_path
,
91 u
'fail_metadata': exc
.metadata
})
93 _log
.warn("No idea what happened here, but it failed: %r", exc
)
94 # Looks like no, so just mark it as failed and don't record a
95 # failure_error (we'll assume it wasn't handled) and don't record
96 # metadata (in fact overwrite it if somehow it had previous info
98 atomic_update(mgg
.database
.MediaEntry
,
100 {u
'state': u
'failed',
102 u
'fail_metadata': {}})
105 class BaseProcessingFail(Exception):
107 Base exception that all other processing failure messages should
110 You shouldn't call this itself; instead you should subclass it
111 and provid the exception_path and general_message applicable to
114 general_message
= u
''
117 def exception_path(self
):
119 self
.__class
__.__module
__, self
.__class
__.__name
__)
121 def __init__(self
, **metadata
):
122 self
.metadata
= metadata
or {}
125 class BadMediaFail(BaseProcessingFail
):
127 Error that should be raised when an inappropriate file was given
128 for the media type specified.
130 general_message
= _(u
'Invalid file given for media type.')