Skip to content

Commit

Permalink
feat: [#296] persist torrent comment
Browse files Browse the repository at this point in the history
and add it to the API responses.

- Torrent details endpoint
- Torrent list endpoint
- Include the comment in the downloaded torrent
  • Loading branch information
josecelano committed Sep 18, 2023
1 parent eb26c8d commit e9476fc
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE torrust_torrents ADD COLUMN comment TEXT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "torrust_torrents" ADD COLUMN "comment" TEXT NULL;
147 changes: 95 additions & 52 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ impl Database for Mysql {
})
}

// TODO: refactor this
// todo: refactor this
#[allow(clippy::too_many_lines)]
async fn get_torrents_search_sorted_paginated(
&self,
search: &Option<String>,
Expand Down Expand Up @@ -375,7 +376,17 @@ impl Database for Mysql {
};

let mut query_string = format!(
"SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded, tt.size AS file_size, tt.name,
"SELECT
tt.torrent_id,
tp.username AS uploader,
tt.info_hash,
ti.title,
ti.description,
tt.category_id,
DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded,
tt.size AS file_size,
tt.name,
tt.comment,
CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders,
CAST(COALESCE(sum(ts.leechers),0) as signed) as leechers
FROM torrust_torrents tt
Expand Down Expand Up @@ -443,31 +454,47 @@ impl Database for Mysql {
};

// add torrent
let torrent_id = query("INSERT INTO torrust_torrents (uploader_id, category_id, info_hash, size, name, pieces, piece_length, private, root_hash, `source`, date_uploaded) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())")
.bind(uploader_id)
.bind(category_id)
.bind(info_hash.to_lowercase())
.bind(torrent.file_size())
.bind(torrent.info.name.to_string())
.bind(pieces)
.bind(torrent.info.piece_length)
.bind(torrent.info.private)
.bind(root_hash)
.bind(torrent.info.source.clone())
.execute(&mut tx)
.await
.map(|v| i64::try_from(v.last_insert_id()).expect("last ID is larger than i64"))
.map_err(|e| match e {
sqlx::Error::Database(err) => {
log::error!("DB error: {:?}", err);
if err.message().contains("Duplicate entry") && err.message().contains("info_hash") {
database::Error::TorrentAlreadyExists
} else {
database::Error::Error
}
let torrent_id = query(
"INSERT INTO torrust_torrents (
uploader_id,
category_id,
info_hash,
size,
name,
pieces,
piece_length,
private,
root_hash,
`source`,
comment,
date_uploaded
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())",
)
.bind(uploader_id)
.bind(category_id)
.bind(info_hash.to_lowercase())
.bind(torrent.file_size())
.bind(torrent.info.name.to_string())
.bind(pieces)
.bind(torrent.info.piece_length)
.bind(torrent.info.private)
.bind(root_hash)
.bind(torrent.info.source.clone())
.bind(torrent.comment.clone())
.execute(&mut tx)
.await
.map(|v| i64::try_from(v.last_insert_id()).expect("last ID is larger than i64"))
.map_err(|e| match e {
sqlx::Error::Database(err) => {
log::error!("DB error: {:?}", err);
if err.message().contains("Duplicate entry") && err.message().contains("info_hash") {
database::Error::TorrentAlreadyExists
} else {
database::Error::Error
}
_ => database::Error::Error
})?;
}
_ => database::Error::Error,
})?;

// add torrent canonical infohash

Expand Down Expand Up @@ -650,23 +677,19 @@ impl Database for Mysql {
}

async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result<DbTorrentInfo, database::Error> {
query_as::<_, DbTorrentInfo>(
"SELECT torrent_id, info_hash, name, pieces, piece_length, private, root_hash FROM torrust_torrents WHERE torrent_id = ?",
)
.bind(torrent_id)
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
query_as::<_, DbTorrentInfo>("SELECT * FROM torrust_torrents WHERE torrent_id = ?")
.bind(torrent_id)
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
}

async fn get_torrent_info_from_info_hash(&self, info_hash: &InfoHash) -> Result<DbTorrentInfo, database::Error> {
query_as::<_, DbTorrentInfo>(
"SELECT torrent_id, info_hash, name, pieces, piece_length, private, root_hash FROM torrust_torrents WHERE info_hash = ?",
)
.bind(info_hash.to_hex_string().to_lowercase())
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
query_as::<_, DbTorrentInfo>("SELECT * FROM torrust_torrents WHERE info_hash = ?")
.bind(info_hash.to_hex_string().to_lowercase())
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
}

async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result<Vec<TorrentFile>, database::Error> {
Expand Down Expand Up @@ -705,38 +728,58 @@ impl Database for Mysql {

async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result<TorrentListing, database::Error> {
query_as::<_, TorrentListing>(
"SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded, tt.size AS file_size, tt.name,
"SELECT
tt.torrent_id,
tp.username AS uploader,
tt.info_hash,
ti.title,
ti.description,
tt.category_id,
DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded,
tt.size AS file_size,
tt.name,
tt.comment,
CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders,
CAST(COALESCE(sum(ts.leechers),0) as signed) as leechers
FROM torrust_torrents tt
INNER JOIN torrust_user_profiles tp ON tt.uploader_id = tp.user_id
INNER JOIN torrust_torrent_info ti ON tt.torrent_id = ti.torrent_id
LEFT JOIN torrust_torrent_tracker_stats ts ON tt.torrent_id = ts.torrent_id
WHERE tt.torrent_id = ?
GROUP BY torrent_id"
GROUP BY torrent_id",
)
.bind(torrent_id)
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
.bind(torrent_id)
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
}

async fn get_torrent_listing_from_info_hash(&self, info_hash: &InfoHash) -> Result<TorrentListing, database::Error> {
query_as::<_, TorrentListing>(
"SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded, tt.size AS file_size, tt.name,
"SELECT
tt.torrent_id,
tp.username AS uploader,
tt.info_hash,
ti.title,
ti.description,
tt.category_id,
DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded,
tt.size AS file_size,
tt.name,
tt.comment,
CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders,
CAST(COALESCE(sum(ts.leechers),0) as signed) as leechers
FROM torrust_torrents tt
INNER JOIN torrust_user_profiles tp ON tt.uploader_id = tp.user_id
INNER JOIN torrust_torrent_info ti ON tt.torrent_id = ti.torrent_id
LEFT JOIN torrust_torrent_tracker_stats ts ON tt.torrent_id = ts.torrent_id
WHERE tt.info_hash = ?
GROUP BY torrent_id"
GROUP BY torrent_id",
)
.bind(info_hash.to_hex_string().to_lowercase())
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
.bind(info_hash.to_hex_string().to_lowercase())
.fetch_one(&self.pool)
.await
.map_err(|_| database::Error::TorrentNotFound)
}

async fn get_all_torrents_compact(&self) -> Result<Vec<TorrentCompact>, database::Error> {
Expand Down
Loading

0 comments on commit e9476fc

Please sign in to comment.