Track unfinished media destinations for channel cleanup

This commit is contained in:
evgeny committed 2026-10-22 14:44:24 +02:00
1 parent 8cba8ef3e6
commit b3a223521c
2 files changed
+48 -6

No files matched your search

+45 -6
View File
@@ -124,6 +124,23 @@ int media_index_set_base(sqlite3* db, const char* base) {
return -1;
}
int media_index_track_preparation(sqlite3* db, const char* chat_id, const char* path, const char* base) {
char location[MEDIA_PATH_MAX]; sqlite3_stmt* st = NULL;
if (!db || !chat_id || !*chat_id || strlen(chat_id) >= 64 || media_index_location(path, base, location)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index invalid preparation path/channel"); return -1;
}
int rc = sqlite3_prepare_v2(db, "INSERT INTO media_preparations(chat_id,location) VALUES(?,?) ON CONFLICT DO NOTHING",
-1, &st, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 2, location, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_step(st);
int finish = st ? sqlite3_finalize(st) : SQLITE_OK;
if (rc == SQLITE_DONE && finish == SQLITE_OK) {
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "media_index preparation tracked chat=%s path=%s", chat_id, location); return 0;
}
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index track preparation chat=%s rc=%d: %s", chat_id, rc, sqlite3_errmsg(db)); return -1;
}
static int mi_insert(sqlite3* db,
const uint8_t* media_id, const uint8_t* block_id,
const uint8_t* content_hash,
@@ -198,7 +215,9 @@ int media_index_init(sqlite3* db) {
return -1;
}
if (mi_exec(db, "CREATE UNIQUE INDEX IF NOT EXISTS idx_media_files_block_id ON media_files(block_id)")
if (mi_exec(db, "CREATE TABLE IF NOT EXISTS media_preparations(chat_id TEXT NOT NULL,location TEXT NOT NULL,"
"PRIMARY KEY(chat_id,location))")
|| mi_exec(db, "CREATE UNIQUE INDEX IF NOT EXISTS idx_media_files_block_id ON media_files(block_id)")
|| mi_exec(db, "CREATE INDEX IF NOT EXISTS idx_media_files_media_id ON media_files(media_id)")
|| mi_exec(db, "CREATE INDEX IF NOT EXISTS idx_media_files_chat_id ON media_files(chat_id)")
|| mi_exec(db, "CREATE INDEX IF NOT EXISTS idx_media_files_node_id ON media_files(node_id)")
@@ -243,9 +262,9 @@ int media_index_delete_channel(sqlite3* db, const char* chat_id, const char* med
struct mi_delete_file { char location[MEDIA_PATH_MAX]; char path[MEDIA_PATH_MAX]; int shared; };
struct mi_delete_file* files = NULL; uint32_t count = 0;
sqlite3_stmt* st = NULL;
const char* query = "SELECT DISTINCT location,EXISTS(SELECT 1 FROM media_files other"
" WHERE other.location=mine.location AND other.chat_id!=mine.chat_id)"
" FROM media_files mine WHERE chat_id=?";
const char* query = "WITH paths AS (SELECT chat_id,location FROM media_files UNION SELECT chat_id,location FROM media_preparations)"
" SELECT location,EXISTS(SELECT 1 FROM paths other WHERE other.location=mine.location AND other.chat_id!=mine.chat_id)"
" FROM paths mine WHERE chat_id=?";
int rc = sqlite3_prepare_v2(db, query, -1, &st, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
if (rc != SQLITE_OK) goto failed;
@@ -265,16 +284,23 @@ int media_index_delete_channel(sqlite3* db, const char* chat_id, const char* med
if (rc != SQLITE_DONE) goto failed;
rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto failed;
rc = sqlite3_prepare_v2(db, "DELETE FROM media_files WHERE chat_id=? AND location=?", -1, &st, NULL);
rc = sqlite3_prepare_v2(db, "DELETE FROM media_preparations WHERE chat_id=? AND location=?", -1, &st, NULL);
if (rc != SQLITE_OK) goto failed;
int errors = 0, rows = 0;
for (uint32_t i = 0; i < count; i++) {
if (!files[i].shared && mi_remove(files[i].path)) { errors++; continue; }
sqlite3_stmt* blocks = NULL;
rc = sqlite3_prepare_v2(db, "DELETE FROM media_files WHERE chat_id=? AND location=?", -1, &blocks, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(blocks, 1, chat_id, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(blocks, 2, files[i].location, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_step(blocks);
int finish = blocks ? sqlite3_finalize(blocks) : SQLITE_OK;
if (rc != SQLITE_DONE || finish != SQLITE_OK) goto failed;
rows += sqlite3_changes(db);
rc = sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 2, files[i].location, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_step(st);
if (rc != SQLITE_DONE) goto failed;
rows += sqlite3_changes(db);
rc = sqlite3_reset(st);
if (rc != SQLITE_OK) goto failed;
}
@@ -346,6 +372,18 @@ static int mi_register_file(sqlite3* db, const struct media_index_result* result
if (mi_insert(db, result->media_id, result->block_ids + (size_t)n * 16, result->content_hash,
chat_id, location, node_id, signature, ts, result->file_size, size, n, offset)) { failed = 1; break; }
}
if (!failed) {
sqlite3_stmt* st = NULL;
int rc = sqlite3_prepare_v2(db, "DELETE FROM media_preparations WHERE chat_id=? AND location=?", -1, &st, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 2, location, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_step(st);
int finish = st ? sqlite3_finalize(st) : SQLITE_OK;
if (rc != SQLITE_DONE || finish != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index clearing preparation failed chat=%s rc=%d: %s", chat_id, rc, sqlite3_errmsg(db));
failed = 1;
}
}
if (mi_end_registration(db, failed)) return -1;
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "media_index registered chat=%s blocks=%d path=%s signed=%d",
chat_id, result->num_blocks, location, key != NULL);
@@ -505,6 +543,7 @@ void media_index_register_async(
|| strlen(media_base) >= sizeof(((struct mi_reg_ctx*)0)->media_base)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index register oversized/empty path"); cb(cb_arg, -1, NULL); return;
}
if (media_index_track_preparation(db, chat_id, dest_path, media_base)) { cb(cb_arg, -1, NULL); return; }
struct mi_reg_ctx* ctx = u_calloc(1, sizeof(*ctx));
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index context allocation failed"); cb(cb_arg, -1, NULL); return; }
+3
View File
@@ -31,6 +31,9 @@ int media_index_path(const char* base, const char* location, char path[MEDIA_PAT
* get возвращает проверенную базу; 0/-1. Смена базы при работающем ядре не поддерживается. */
int media_index_set_base(sqlite3* db, const char* base);
int media_index_get_base(sqlite3* db, char base[MEDIA_BASE_MAX]);
/* Учесть назначение до файловой подготовки. Ошибка подготовки оставляет запись для delete_channel;
* успешная регистрация файла удаляет её в той же транзакции. Сам файл здесь не меняется. 0/-1. */
int media_index_track_preparation(sqlite3* db, const char* chat_id, const char* path, const char* base);
struct media_index_result {
uint8_t media_id[16];