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 class ProgressCallback(object):
29 def __init__(self
, entry
):
32 def __call__(self
, progress
):
34 self
.entry
.transcoding_progress
= progress
38 def create_pub_filepath(entry
, filename
):
39 return mgg
.public_store
.get_unique_filepath(
45 class FilenameBuilder(object):
46 """Easily slice and dice filenames.
48 Initialize this class with an original file path, then use the fill()
49 method to create new filenames based on the original.
52 MAX_FILENAME_LENGTH
= 255 # VFAT's maximum filename length
54 def __init__(self
, path
):
55 """Initialize a builder from an original file path."""
56 self
.dirpath
, self
.basename
= os
.path
.split(path
)
57 self
.basename
, self
.ext
= os
.path
.splitext(self
.basename
)
58 self
.ext
= self
.ext
.lower()
60 def fill(self
, fmtstr
):
61 """Build a new filename based on the original.
63 The fmtstr argument can include the following:
64 {basename} -- the original basename, with the extension removed
65 {ext} -- the original extension, always lowercase
67 If necessary, {basename} will be truncated so the filename does not
68 exceed this class' MAX_FILENAME_LENGTH in length.
71 basename_len
= (self
.MAX_FILENAME_LENGTH
-
72 len(fmtstr
.format(basename
='', ext
=self
.ext
)))
73 return fmtstr
.format(basename
=self
.basename
[:basename_len
],
77 class ProcessingState(object):
79 The first and only argument to the "processor" of a media type
81 This could be thought of as a "request" to the processor
82 function. It has the main info for the request (media entry)
83 and a bunch of tools for the request on it.
84 It can get more fancy without impacting old media types.
86 def __init__(self
, entry
):
89 self
.queued_filename
= None
91 def set_workbench(self
, wb
):
94 def get_queued_filename(self
):
96 Get the a filename for the original, on local storage
98 if self
.queued_filename
is not None:
99 return self
.queued_filename
100 queued_filepath
= self
.entry
.queued_media_file
101 queued_filename
= self
.workbench
.localized_file(
102 mgg
.queue_store
, queued_filepath
,
104 self
.queued_filename
= queued_filename
105 return queued_filename
107 def copy_original(self
, target_name
, keyname
=u
"original"):
108 self
.store_public(keyname
, self
.get_queued_filename(), target_name
)
110 def store_public(self
, keyname
, local_file
, target_name
=None):
111 if target_name
is None:
112 target_name
= os
.path
.basename(local_file
)
113 target_filepath
= create_pub_filepath(self
.entry
, target_name
)
114 if keyname
in self
.entry
.media_files
:
115 _log
.warn("store_public: keyname %r already used for file %r, "
116 "replacing with %r", keyname
,
117 self
.entry
.media_files
[keyname
], target_filepath
)
118 mgg
.public_store
.copy_local_to_storage(local_file
, target_filepath
)
119 self
.entry
.media_files
[keyname
] = target_filepath
121 def delete_queue_file(self
):
122 # Remove queued media file from storage and database.
123 # queued_filepath is in the task_id directory which should
124 # be removed too, but fail if the directory is not empty to be on
125 # the super-safe side.
126 queued_filepath
= self
.entry
.queued_media_file
127 mgg
.queue_store
.delete_file(queued_filepath
) # rm file
128 mgg
.queue_store
.delete_dir(queued_filepath
[:-1]) # rm dir
129 self
.entry
.queued_media_file
= []
132 def mark_entry_failed(entry_id
, exc
):
134 Mark a media entry as having failed in its conversion.
136 Uses the exception that was raised to mark more information. If
137 the exception is a derivative of BaseProcessingFail then we can
138 store extra information that can be useful for users telling them
139 why their media failed to process.
142 - entry_id: The id of the media entry
145 # Was this a BaseProcessingFail? In other words, was this a
146 # type of error that we know how to handle?
147 if isinstance(exc
, BaseProcessingFail
):
148 # Looks like yes, so record information about that failure and any
149 # metadata the user might have supplied.
150 atomic_update(mgg
.database
.MediaEntry
,
152 {u
'state': u
'failed',
153 u
'fail_error': unicode(exc
.exception_path
),
154 u
'fail_metadata': exc
.metadata
})
156 _log
.warn("No idea what happened here, but it failed: %r", exc
)
157 # Looks like no, so just mark it as failed and don't record a
158 # failure_error (we'll assume it wasn't handled) and don't record
159 # metadata (in fact overwrite it if somehow it had previous info
161 atomic_update(mgg
.database
.MediaEntry
,
163 {u
'state': u
'failed',
165 u
'fail_metadata': {}})
168 class BaseProcessingFail(Exception):
170 Base exception that all other processing failure messages should
173 You shouldn't call this itself; instead you should subclass it
174 and provid the exception_path and general_message applicable to
177 general_message
= u
''
180 def exception_path(self
):
182 self
.__class
__.__module
__, self
.__class
__.__name
__)
184 def __init__(self
, **metadata
):
185 self
.metadata
= metadata
or {}
187 class BadMediaFail(BaseProcessingFail
):
189 Error that should be raised when an inappropriate file was given
190 for the media type specified.
192 general_message
= _(u
'Invalid file given for media type.')