|
40 | 40 | import flask_migrate |
41 | 41 | import requests |
42 | 42 | from werkzeug.utils import secure_filename |
| 43 | +import shutil |
43 | 44 |
|
44 | 45 | app = Flask(__name__) |
45 | 46 | app.config.update({ |
@@ -1056,7 +1057,7 @@ def render_dir(dir_id: int, auth_dict: Optional[Dict[str, Any]] = None): |
1056 | 1057 | dir_model = Directory.query.filter(Directory.id == dir_id).first() |
1057 | 1058 | if dir_model is None: |
1058 | 1059 | abort(404) |
1059 | | - description = dir_model.description |
| 1060 | + description = dir_model.description or "" |
1060 | 1061 | display_description = len(description) > 0 |
1061 | 1062 |
|
1062 | 1063 | display_parent = True |
@@ -1104,7 +1105,7 @@ def render_file(file_id: int, auth_dict: Optional[Dict[str, Any]] = None): |
1104 | 1105 | if gallery_lockdown and (not auth_dict['is_eboard'] and not auth_dict['is_rtp']): |
1105 | 1106 | abort(405) |
1106 | 1107 |
|
1107 | | - description = file_model.caption |
| 1108 | + description = file_model.caption or "" |
1108 | 1109 | display_description = len(description) > 0 |
1109 | 1110 | display_parent = True |
1110 | 1111 | if file_model is None or file_model.parent is None: |
@@ -1139,6 +1140,106 @@ def render_file(file_id: int, auth_dict: Optional[Dict[str, Any]] = None): |
1139 | 1140 | lockdown=gallery_lockdown) |
1140 | 1141 |
|
1141 | 1142 |
|
| 1143 | +@app.route('/upload/chunk', methods=['POST']) |
| 1144 | +@auth.oidc_auth('default') |
| 1145 | +@gallery_auth |
| 1146 | +def upload_chunk(auth_dict: Optional[Dict[str, Any]] = None): |
| 1147 | + chunk = request.files.get('gallery-upload') |
| 1148 | + if chunk is None: |
| 1149 | + return jsonify({'error': 'no chunk'}), 400 |
| 1150 | + |
| 1151 | + dz_uuid = request.form.get('dzuuid') |
| 1152 | + chunk_index = int(request.form.get('dzchunkindex', 0)) |
| 1153 | + chunk_size = int(request.form.get('dzchunksize', 0)) |
| 1154 | + filename = secure_filename(request.form.get('dzfilename', '')) |
| 1155 | + |
| 1156 | + if not dz_uuid or not filename: |
| 1157 | + return jsonify({'error': 'missing chunk metadata'}), 400 |
| 1158 | + |
| 1159 | + chunk_dir = os.path.join(tempfile.gettempdir(), 'chonks', dz_uuid) |
| 1160 | + os.makedirs(chunk_dir, exist_ok=True) |
| 1161 | + |
| 1162 | + out_path = os.path.join(chunk_dir, 'assembled') |
| 1163 | + with open(out_path, 'ab') as out: |
| 1164 | + out.write(chunk.read()) |
| 1165 | + |
| 1166 | + return jsonify({'status': 'ok', 'chunk': chunk_index}), 200 |
| 1167 | + |
| 1168 | + |
| 1169 | +@app.route('/upload/chunk/finalize', methods=['POST']) |
| 1170 | +@auth.oidc_auth('default') |
| 1171 | +@gallery_auth |
| 1172 | +def finalize_upload(auth_dict: Optional[Dict[str, Any]] = None): |
| 1173 | + assert auth_dict is not None |
| 1174 | + owner = auth_dict['uuid'] |
| 1175 | + parent = request.form.get('parent_id') |
| 1176 | + dz_uuid = request.form.get('dzuuid') |
| 1177 | + filename = secure_filename(request.form.get('filename', '')) |
| 1178 | + total_chunks = int(request.form.get('dztotalchunkcount', 0)) |
| 1179 | + |
| 1180 | + if not all([parent, dz_uuid, filename, total_chunks]): |
| 1181 | + return jsonify({'error': 'missing parameters'}), 400 |
| 1182 | + |
| 1183 | + chunk_dir = os.path.join(tempfile.gettempdir(), 'chonks', dz_uuid) |
| 1184 | + |
| 1185 | + upload_status: Dict[str, Any] = {} |
| 1186 | + upload_status['redirect'] = '/view/dir/' + str(parent) |
| 1187 | + errors: List[str] = [] |
| 1188 | + success: List[Dict[str, Any]] = [] |
| 1189 | + |
| 1190 | + file_model = File.query.filter(File.parent == parent) \ |
| 1191 | + .filter(File.name == filename).first() |
| 1192 | + if file_model is not None: |
| 1193 | + errors.append(filename) |
| 1194 | + upload_status['error'] = errors |
| 1195 | + upload_status['success'] = success |
| 1196 | + return jsonify(upload_status), 200 |
| 1197 | + |
| 1198 | + dir_path = tempfile.mkdtemp() |
| 1199 | + filepath = os.path.join(dir_path, filename) |
| 1200 | + try: |
| 1201 | + assembled_path = os.path.join(chunk_dir, 'assembled') |
| 1202 | + if not os.path.exists(assembled_path): |
| 1203 | + return jsonify({'error': 'assembled file missing'}), 400 |
| 1204 | + shutil.move(assembled_path, filepath) |
| 1205 | + try: |
| 1206 | + mime, file_model = add_file(filename, dir_path, parent, '', owner) |
| 1207 | + except OSError as e: |
| 1208 | + if e.errno == 28: |
| 1209 | + return jsonify({'error': 'storage full'}), 507 |
| 1210 | + raise |
| 1211 | + if file_model is None: |
| 1212 | + errors.append(filename) |
| 1213 | + else: |
| 1214 | + with open(filepath, 'rb') as f_hnd: |
| 1215 | + storage_interface.put( |
| 1216 | + 'files/{}'.format(file_model.s3_id), |
| 1217 | + f_hnd, |
| 1218 | + filename, |
| 1219 | + mime |
| 1220 | + ) |
| 1221 | + os.remove(filepath) |
| 1222 | + |
| 1223 | + thumb_path = os.path.join(dir_path, file_model.thumbnail_uuid) |
| 1224 | + with open(thumb_path, 'rb') as f_hnd: |
| 1225 | + storage_interface.put( |
| 1226 | + 'thumbnails/' + file_model.s3_id, |
| 1227 | + f_hnd, |
| 1228 | + 'thumb_' + filename + '.' + thumb_path.split('.')[-1], |
| 1229 | + 'image/gif' if thumb_path.endswith('.gif') else 'image/jpeg' |
| 1230 | + ) |
| 1231 | + os.remove(thumb_path) |
| 1232 | + success.append({'name': file_model.name, 'id': file_model.id}) |
| 1233 | + finally: |
| 1234 | + shutil.rmtree(chunk_dir, ignore_errors=True) |
| 1235 | + shutil.rmtree(dir_path, ignore_errors=True) |
| 1236 | + |
| 1237 | + upload_status['error'] = errors |
| 1238 | + upload_status['success'] = success |
| 1239 | + refresh_default_thumbnails() |
| 1240 | + return jsonify(upload_status), 200 |
| 1241 | + |
| 1242 | + |
1142 | 1243 | @app.route("/view/random_file") |
1143 | 1244 | @auth.oidc_auth('default') |
1144 | 1245 | def get_random_file(): |
|
0 commit comments