Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist torrent comment #298

Merged
merged 7 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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