Skip to content

Commit

Permalink
Merge #298: Persist torrent comment
Browse files Browse the repository at this point in the history
8821346 doc: add some comments for BEP 30 implementation (Jose Celano)
dfdac19 refator: [#296] move logic to service layer (Jose Celano)
1660fd5 refactor: [#296] rename vars (Jose Celano)
b6fe36b refactor: [#296] extract duplicate code (Jose Celano)
f0ad6a4 refactor: rename structs and reorganize mods (Jose Celano)
e9476fc feat: [#296] persist torrent comment (Jose Celano)
eb26c8d fix: tag name for random tag in tests (Jose Celano)

Pull request description:

  And add it to the API responses.

  - Torrent details endpoint
  - Torrent list endpoint
  - Include the `comment` in the downloaded torrent

ACKs for top commit:
  josecelano:
    ACK 8821346

Tree-SHA512: d2203d759ab6a8eaeca413928b580d5be26ebb4041d9f11067a223a187cfd606c4d4380f41e935c3516d635a6225534abba88d5696f18f5c17d72e899d69291f
  • Loading branch information
josecelano committed Sep 19, 2023
2 parents bf989a1 + 8821346 commit ef38ac6
Show file tree
Hide file tree
Showing 14 changed files with 440 additions and 297 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;
26 changes: 9 additions & 17 deletions src/databases/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::models::category::CategoryId;
use crate::models::info_hash::InfoHash;
use crate::models::response::TorrentsResponse;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::{DbTorrentInfo, Torrent, TorrentFile};
use crate::models::torrent_file::{DbTorrent, Torrent, TorrentFile};
use crate::models::torrent_tag::{TagId, TorrentTag};
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
Expand Down Expand Up @@ -203,32 +203,24 @@ pub trait Database: Sync + Send {

/// Get `Torrent` from `InfoHash`.
async fn get_torrent_from_info_hash(&self, info_hash: &InfoHash) -> Result<Torrent, Error> {
let torrent_info = self.get_torrent_info_from_info_hash(info_hash).await?;
let db_torrent = self.get_torrent_info_from_info_hash(info_hash).await?;

let torrent_files = self.get_torrent_files_from_id(torrent_info.torrent_id).await?;
let torrent_files = self.get_torrent_files_from_id(db_torrent.torrent_id).await?;

let torrent_announce_urls = self.get_torrent_announce_urls_from_id(torrent_info.torrent_id).await?;
let torrent_announce_urls = self.get_torrent_announce_urls_from_id(db_torrent.torrent_id).await?;

Ok(Torrent::from_db_info_files_and_announce_urls(
torrent_info,
torrent_files,
torrent_announce_urls,
))
Ok(Torrent::from_database(&db_torrent, &torrent_files, torrent_announce_urls))
}

/// Get `Torrent` from `torrent_id`.
async fn get_torrent_from_id(&self, torrent_id: i64) -> Result<Torrent, Error> {
let torrent_info = self.get_torrent_info_from_id(torrent_id).await?;
let db_torrent = self.get_torrent_info_from_id(torrent_id).await?;

let torrent_files = self.get_torrent_files_from_id(torrent_id).await?;

let torrent_announce_urls = self.get_torrent_announce_urls_from_id(torrent_id).await?;

Ok(Torrent::from_db_info_files_and_announce_urls(
torrent_info,
torrent_files,
torrent_announce_urls,
))
Ok(Torrent::from_database(&db_torrent, &torrent_files, torrent_announce_urls))
}

/// It returns the list of all infohashes producing the same canonical
Expand Down Expand Up @@ -257,10 +249,10 @@ pub trait Database: Sync + Send {
async fn add_info_hash_to_canonical_info_hash_group(&self, original: &InfoHash, canonical: &InfoHash) -> Result<(), Error>;

/// Get torrent's info as `DbTorrentInfo` from `torrent_id`.
async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result<DbTorrentInfo, Error>;
async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result<DbTorrent, Error>;

/// Get torrent's info as `DbTorrentInfo` from torrent `InfoHash`.
async fn get_torrent_info_from_info_hash(&self, info_hash: &InfoHash) -> Result<DbTorrentInfo, Error>;
async fn get_torrent_info_from_info_hash(&self, info_hash: &InfoHash) -> Result<DbTorrent, Error>;

/// Get all torrent's files as `Vec<TorrentFile>` from `torrent_id`.
async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result<Vec<TorrentFile>, Error>;
Expand Down
153 changes: 98 additions & 55 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::models::category::CategoryId;
use crate::models::info_hash::InfoHash;
use crate::models::response::TorrentsResponse;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::{DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentInfo, Torrent, TorrentFile};
use crate::models::torrent_file::{DbTorrent, DbTorrentAnnounceUrl, DbTorrentFile, Torrent, TorrentFile};
use crate::models::torrent_tag::{TagId, TorrentTag};
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
Expand Down 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 @@ -649,24 +676,20 @@ impl Database for Mysql {
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

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)
async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result<DbTorrent, database::Error> {
query_as::<_, DbTorrent>("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)
async fn get_torrent_info_from_info_hash(&self, info_hash: &InfoHash) -> Result<DbTorrent, database::Error> {
query_as::<_, DbTorrent>("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 ef38ac6

Please sign in to comment.