Files
ops-Gazelle/app/User/Bookmark.php
2025-08-27 16:44:44 +02:00

346 lines
12 KiB
PHP

<?php
namespace Gazelle\User;
use Gazelle\Artist;
use Gazelle\Collage;
use Gazelle\Request;
use Gazelle\TGroup;
class Bookmark extends \Gazelle\BaseUser {
final public const tableName = 'pm_conversations_users'; // not really
protected array $all;
public function flush(): static {
$this->user()->flush();
return $this;
}
/**
* Get the bookmark schema.
* Recommended usage:
* [$table, $column] = $bookmark->schema('torrent');
*
* @param string $type the type to get the schema for
*/
public function schema($type): array {
return match ($type) {
'artist' => ['bookmarks_artists', 'ArtistID'],
'collage' => ['bookmarks_collages', 'CollageID'],
'request' => ['bookmarks_requests', 'RequestID'],
'torrent' => ['bookmarks_torrents', 'GroupID'],
default => [null, null],
};
}
/**
* Bookmark an object by a user
*
* @param string $type (on of artist, collage, request, torrent)
* @param int $id The ID of the object
*/
public function create(string $type, int $id): bool {
[$table, $column] = $this->schema($type);
if (
(bool)self::$db->scalar("
SELECT 1 FROM $table WHERE UserID = ? AND $column = ?
", $this->user->id, $id
)
) {
// overbooked
return false;
}
switch ($type) {
case 'torrent':
self::$db->prepared_query("
INSERT IGNORE INTO bookmarks_torrents
(GroupID, UserID, Sort)
VALUES (?, ?,
(1 + coalesce((SELECT max(m.Sort) from bookmarks_torrents m WHERE m.UserID = ?), 0))
)", $id, $this->user->id, $this->user->id
);
self::$cache->delete_multi(["u_book_t_" . $this->user->id, "bookmarks_{$type}_" . $this->user->id, "bookmarks_group_ids_" . $this->user->id]);
$torMan = new \Gazelle\Manager\Torrent();
$tgroup = new \Gazelle\Manager\TGroup()->findById($id);
$tgroup->stats()->increment('bookmark_total');
// RSS feed stuff
$Feed = new \Gazelle\Feed();
foreach ($tgroup->torrentIdList() as $id) {
$torrent = $torMan->findById($id);
if (is_null($torrent)) {
continue;
}
$Feed->populate('torrents_bookmarks_t_' . $this->user->announceKey(),
$Feed->item(
"{$torrent->name()} [{$torrent->label($this->user)}]",
\Text::strip_bbcode($tgroup->description()),
"torrents.php?action=download&id={$id}&torrent_pass=[[PASSKEY]]",
date('r'),
$this->user->username(),
$torrent->group()->location(),
implode(',', $tgroup->tagNameList()),
)
);
}
break;
case 'request':
self::$db->prepared_query("
INSERT IGNORE INTO bookmarks_requests (RequestID, UserID) VALUES (?, ?)
", $id, $this->user->id
);
self::$cache->delete_value("bookmarks_{$type}_" . $this->user->id);
break;
default:
self::$db->prepared_query("
INSERT IGNORE INTO $table ($column, UserID) VALUES (?, ?)
", $id, $this->user->id
);
self::$cache->delete_value("bookmarks_{$type}_" . $this->user->id);
break;
}
return true;
}
/**
* Fetch all bookmarks of a certain type for a user.
* This may seem like an inefficient way to go about this, but it means
* that the database is only hit once, no matter how * many checks are
* made (and most pages where this is needed may have dozens)...
*
* @param string $type type of bookmarks to fetch
* @return array the bookmarks
*/
public function allBookmarks(string $type): array {
if (isset($this->all)) {
return $this->all;
}
$key = "bookmarks_{$type}_" . $this->user->id;
$all = self::$cache->get_value($key);
if ($all === false) {
[$table, $column] = $this->schema($type);
$q = self::$db->get_query_id();
self::$db->prepared_query("
SELECT $column
FROM $table
WHERE UserID = ?
", $this->user->id
);
$all = self::$db->collect($column);
self::$db->set_query_id($q);
self::$cache->cache_value($key, $all, 0);
}
$this->all = $all;
return $this->all;
}
/**
* Check if an artist is bookmarked by a user
*/
public function isArtistBookmarked(Artist $artist): bool {
return in_array($artist->id, $this->allBookmarks('artist'));
}
/**
* Check if a collage is bookmarked by a user
*/
public function isCollageBookmarked(Collage $collage): bool {
return in_array($collage->id, $this->allBookmarks('collage'));
}
/**
* Check if a request is bookmarked by a user
*/
public function isRequestBookmarked(Request $request): bool {
return in_array($request->id, $this->allBookmarks('request'));
}
/**
* Check if an torrent is bookmarked by a user
*/
public function isTGroupBookmarked(TGroup $tgroup): bool {
return in_array($tgroup->id, $this->allBookmarks('torrent'));
}
/**
* Returns an array with User Bookmark data: group IDs, collage data, torrent data
* @return array Group IDs, Bookmark Data, Torrent List
*/
public function tgroupBookmarkList(): array {
$key = "bookmarks_group_ids_" . $this->user->id;
$bookmarkList = self::$cache->get_value($key);
$bookmarkList = false;
self::$db->prepared_query("
SELECT b.GroupID AS tgroup_id,
b.Sort AS sequence,
b.Time AS created
FROM bookmarks_torrents b
WHERE b.UserID = ?
ORDER BY b.Sort, b.Time
", $this->user->id
);
$bookmarkList = self::$db->to_array(false, MYSQLI_ASSOC);
self::$cache->cache_value($key, $bookmarkList, 3600);
return $bookmarkList;
}
public function tgroupArtistLeaderboard(
\Gazelle\Manager\Artist $artistMan = new \Gazelle\Manager\Artist(),
): array {
self::$db->prepared_query("
SELECT aa.ArtistID AS id,
count(*) AS total
FROM bookmarks_torrents b
INNER JOIN torrents_artists ta USING (GroupID)
INNER JOIN artists_alias aa USING (AliasID)
WHERE b.userid = ?
GROUP BY aa.ArtistID
ORDER BY total DESC, id
LIMIT 10
", $this->user->id
);
$result = self::$db->to_array(false, MYSQLI_ASSOC);
$list = [];
foreach ($result as $item) {
$artist = $artistMan->findById($item['id']);
if ($artist) {
$item['name'] = $artist->name();
$list[] = $item;
}
}
return $list;
}
public function tgroupArtistTotal(): int {
return (int)self::$db->scalar("
SELECT count(*) AS total
FROM bookmarks_torrents b
INNER JOIN torrents_artists ta USING (GroupID)
WHERE b.UserID = ?
", $this->user->id
);
}
public function tgroupTagLeaderboard(): array {
self::$db->prepared_query("
SELECT t.Name AS name,
count(*) AS total
FROM bookmarks_torrents b
INNER JOIN torrents_tags ta USING (GroupID)
INNER JOIN tags t ON (t.ID = ta.TagID)
WHERE b.UserID = ?
GROUP BY t.Name
ORDER By 2 desc, t.Name
LIMIT 10
", $this->user->id
);
return self::$db->to_array(false, MYSQLI_ASSOC);
}
public function torrentTotal(): int {
return (int)self::$db->scalar("
SELECT count(*)
FROM bookmarks_torrents b
INNER JOIN torrents t USING (GroupID)
WHERE b.UserID = ?
", $this->user->id
);
}
public function artistList(): array {
self::$db->prepared_query("
SELECT ag.ArtistID AS artist_id,
aa.Name AS artist_name,
ba.Time AS created
FROM bookmarks_artists ba
INNER JOIN artists_group ag USING (ArtistID)
INNER JOIN artists_alias aa ON (ag.PrimaryAlias = aa.AliasID)
WHERE ba.UserID = ?
ORDER BY aa.Name
", $this->user->id
);
return self::$db->to_array(false, MYSQLI_ASSOC);
}
/**
* Returns an array of torrent bookmarks
* @return array containing [group_id, seq, added, torrent_id]
*/
public function tgroupList(int $limit, int $offset): array {
self::$db->prepared_query("
SELECT b.GroupID AS tgroup_id,
b.Sort AS seq,
b.Time AS added,
group_concat(t.ID ORDER BY
t.GroupID, t.Remastered, (t.RemasterYear != 0) DESC, t.RemasterYear, t.RemasterTitle,
t.RemasterRecordLabel, t.RemasterCatalogueNumber, t.Media, t.Format, t.Encoding, t.ID
) AS torrent_list
FROM bookmarks_torrents b
INNER JOIN torrents t USING (GroupID)
WHERE b.UserID = ?
GROUP BY b.GroupID, b.Sort, b.Time
ORDER BY seq, added
LIMIT ? OFFSET ?
", $this->user->id, $limit, $offset
);
return self::$db->to_array(false, MYSQLI_ASSOC);
}
public function removeSnatched(): int {
self::$db->prepared_query("
DELETE b
FROM bookmarks_torrents AS b
INNER JOIN (
SELECT DISTINCT t.GroupID
FROM torrents AS t
INNER JOIN xbt_snatched AS s ON (s.fid = t.ID)
WHERE s.uid = ?
) AS s USING (GroupID)
WHERE b.UserID = ?
", $this->user->id, $this->user->id
);
self::$cache->delete_value("bookmarks_group_ids_" . $this->user->id);
return self::$db->affected_rows();
}
/**
* Remove a bookmark of an object by a user
*/
public function removeObject(string $type, int $id): int {
[$table, $column] = $this->schema($type);
self::$db->prepared_query("
DELETE FROM $table WHERE UserID = ? AND $column = ?
", $this->user->id, $id
);
$affected = self::$db->affected_rows();
self::$cache->delete_multi(["u_book_t_" . $this->user->id, "bookmarks_{$type}_" . $this->user->id]);
if ($type === 'torrent' && self::$db->affected_rows()) {
self::$cache->delete_value("bookmarks_group_ids_" . $this->user->id);
new \Gazelle\TGroup($id)->stats()->increment('bookmark_total', -1);
}
return $affected;
}
public function remove(): int {
$affected = 0;
foreach (
[
'bookmarks_artists',
'bookmarks_collages',
'bookmarks_requests',
'bookmarks_torrents',
] as $table
) {
self::$db->prepared_query("
DELETE FROM $table WHERE UserID = ?
", $this->id
);
$affected += self::$db->affected_rows();
}
return $affected;
}
}