mirror of
https://github.com/OPSnet/Gazelle.git
synced 2026-01-16 18:04:34 -05:00
Add user_torrents ajax endpoint
This commit is contained in:
@@ -80,6 +80,32 @@ abstract class ArtistRole extends Base {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the id of the first artist that would be shown in the renderRole
|
||||||
|
* display. If it would be shown as "various ...", then it's the first
|
||||||
|
* artist of that role type.
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function primaryId(): int|null {
|
||||||
|
$roleList = $this->roleList();
|
||||||
|
$composerCount = count($roleList['composer'] ?? []);
|
||||||
|
$conductorCount = count($roleList['conductor'] ?? []);
|
||||||
|
$djCount = count($roleList['dj'] ?? []);
|
||||||
|
$mainCount = count($roleList['main'] ?? []);
|
||||||
|
|
||||||
|
if ($djCount) {
|
||||||
|
return $roleList['dj'][0]['id'];
|
||||||
|
} elseif ($composerCount) {
|
||||||
|
return $roleList['composer'][0]['id'];
|
||||||
|
} elseif ($mainCount) {
|
||||||
|
return $roleList['main'][0]['id'];
|
||||||
|
} elseif ($conductorCount) {
|
||||||
|
return $roleList['conductor'][0]['id'];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function renderRole(int $mode): string {
|
protected function renderRole(int $mode): string {
|
||||||
$roleList = $this->roleList();
|
$roleList = $this->roleList();
|
||||||
$arrangerCount = count($roleList['arranger'] ?? []);
|
$arrangerCount = count($roleList['arranger'] ?? []);
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
namespace Gazelle\Enum;
|
namespace Gazelle\Enum;
|
||||||
|
|
||||||
enum UserTorrentSearch: string {
|
enum UserTorrentSearch: string {
|
||||||
case seeding = 'seeding';
|
case downloaded = 'downloaded';
|
||||||
case snatched = 'snatched';
|
case leeching = 'leeching';
|
||||||
case uploaded = 'uploaded';
|
case seeding = 'seeding';
|
||||||
|
case snatched = 'snatched';
|
||||||
|
case snatchedUnseeded = 'snatched-unseeded';
|
||||||
|
case uploaded = 'uploaded';
|
||||||
|
case uploadedUnseeded = 'uploaded-unseeded';
|
||||||
}
|
}
|
||||||
|
|||||||
91
app/Json/UserTorrents.php
Normal file
91
app/Json/UserTorrents.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Gazelle\Json;
|
||||||
|
|
||||||
|
use Gazelle\Search\UserTorrent;
|
||||||
|
use Gazelle\Enum\UserTorrentSearch;
|
||||||
|
|
||||||
|
class UserTorrents extends \Gazelle\Json {
|
||||||
|
protected int $limit = 500;
|
||||||
|
protected int $offset = 0;
|
||||||
|
protected string $type = 'seeding';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected \Gazelle\User $user,
|
||||||
|
protected \Gazelle\User $viewer,
|
||||||
|
protected \Gazelle\Manager\Torrent $torMan = new \Gazelle\Manager\Torrent(),
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function setType(string $type): static {
|
||||||
|
$this->type = $type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLimit(int $limit): static {
|
||||||
|
$this->limit = $limit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOffset(int $offset): static {
|
||||||
|
$this->offset = $offset;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function payload(): array {
|
||||||
|
switch ($this->type) {
|
||||||
|
case 'downloaded':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::downloaded);
|
||||||
|
break;
|
||||||
|
case 'leeching':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::leeching);
|
||||||
|
break;
|
||||||
|
case 'seeding':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::seeding);
|
||||||
|
break;
|
||||||
|
case 'snatched':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::snatched);
|
||||||
|
break;
|
||||||
|
case 'snatched-unseeded':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::snatchedUnseeded);
|
||||||
|
break;
|
||||||
|
case 'uploaded':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::uploaded);
|
||||||
|
break;
|
||||||
|
case 'uploaded-unseeded':
|
||||||
|
$userTorrent = new UserTorrent($this->user, UserTorrentSearch::uploadedUnseeded);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
json_error("bad type");
|
||||||
|
}
|
||||||
|
$userTorrent->setLimit($this->limit)->setOffset($this->offset);
|
||||||
|
|
||||||
|
return [
|
||||||
|
"$this->type" => array_reduce(
|
||||||
|
$userTorrent->idList(),
|
||||||
|
function ($acc, $id) {
|
||||||
|
$torrent = $this->torMan->findById($id);
|
||||||
|
if ($torrent) {
|
||||||
|
$item = [
|
||||||
|
'groupId' => $torrent->groupId(),
|
||||||
|
'torrentId' => $torrent->id(),
|
||||||
|
'name' => $torrent->group()->name(),
|
||||||
|
'torrentSize' => $torrent->size(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($torrent->group()->hasArtistRole()) {
|
||||||
|
// artistId and artistName are returned mostly for compatibility with RED
|
||||||
|
// as people should probably just use `artists` array principally.
|
||||||
|
$item['artistId'] = $torrent->group()->artistRole()?->primaryId();
|
||||||
|
$item['artistName'] = $torrent->group()->artistName();
|
||||||
|
$item['artists'] = static::artistPayload($torrent->group());
|
||||||
|
}
|
||||||
|
$acc[] = $item;
|
||||||
|
}
|
||||||
|
return $acc;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
'total' => $userTorrent->total(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,9 @@ use Gazelle\Enum\UserTorrentSearch;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class UserTorrent extends \Gazelle\Base {
|
class UserTorrent extends \Gazelle\Base {
|
||||||
|
private int $limit;
|
||||||
|
private int $offset;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected \Gazelle\User $user,
|
protected \Gazelle\User $user,
|
||||||
protected UserTorrentSearch $type,
|
protected UserTorrentSearch $type,
|
||||||
@@ -20,22 +23,74 @@ class UserTorrent extends \Gazelle\Base {
|
|||||||
return $this->type->value;
|
return $this->type->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function idList(): array {
|
public function setLimit(int $limit): static {
|
||||||
self::$db->prepared_query("
|
$this->limit = $limit;
|
||||||
SELECT DISTINCT t.ID
|
return $this;
|
||||||
FROM torrents AS t "
|
}
|
||||||
|
|
||||||
|
public function setOffset(int $offset): static {
|
||||||
|
$this->offset = $offset;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sql(): string {
|
||||||
|
return "FROM torrents AS t "
|
||||||
. match ($this->type) {
|
. match ($this->type) {
|
||||||
|
UserTorrentSearch::downloaded => "
|
||||||
|
INNER JOIN users_downloads AS ud ON (ud.TorrentID = t.ID)
|
||||||
|
WHERE ud.UserID = ?
|
||||||
|
ORDER BY ud.Time DESC",
|
||||||
|
UserTorrentSearch::leeching => "
|
||||||
|
INNER JOIN xbt_files_users AS xfu ON (xfu.fid = t.ID)
|
||||||
|
WHERE xfu.active = 1 AND xfu.Remaining > 0 AND xfu.uid = ?
|
||||||
|
ORDER BY from_unixtime(xfu.mtime - xfu.timespent) DESC",
|
||||||
UserTorrentSearch::seeding => "
|
UserTorrentSearch::seeding => "
|
||||||
INNER JOIN xbt_files_users AS xfu ON (t.ID = xfu.fid)
|
INNER JOIN xbt_files_users AS xfu ON (t.ID = xfu.fid)
|
||||||
WHERE xfu.remaining = 0
|
WHERE xfu.remaining = 0 AND xfu.uid = ?
|
||||||
AND xfu.uid = ?",
|
ORDER BY from_unixtime(xfu.mtime - xfu.timespent) DESC",
|
||||||
UserTorrentSearch::snatched => "
|
UserTorrentSearch::snatched => "
|
||||||
INNER JOIN xbt_snatched AS x ON (t.ID = x.fid)
|
INNER JOIN xbt_snatched AS xs ON (t.ID = xs.fid)
|
||||||
WHERE x.uid = ?",
|
WHERE xs.uid = ?
|
||||||
default =>
|
ORDER BY from_unixtime(xs.tstamp) DESC",
|
||||||
"WHERE t.UserID = ?",
|
UserTorrentSearch::snatchedUnseeded => "
|
||||||
}, $this->user->id
|
INNER JOIN xbt_snatched AS xs ON (xs.fid = t.ID)
|
||||||
);
|
LEFT JOIN xbt_files_users AS xfu USING (uid, fid)
|
||||||
|
WHERE xfu.fid IS NULL AND xs.uid = ?
|
||||||
|
ORDER BY from_unixtime(xs.tstamp) DESC",
|
||||||
|
UserTorrentSearch::uploadedUnseeded => "
|
||||||
|
LEFT JOIN xbt_files_users AS xfu ON (xfu.fid = t.ID AND xfu.uid = t.UserID)
|
||||||
|
WHERE xfu.fid IS NULL AND t.UserID = ?
|
||||||
|
ORDER BY t.created DESC",
|
||||||
|
// UserTorrentSearch::uploaded
|
||||||
|
default => "
|
||||||
|
WHERE t.UserID = ?
|
||||||
|
ORDER BY t.created DESC",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function total(): int {
|
||||||
|
return (int)self::$db->scalar("
|
||||||
|
SELECT COUNT(DISTINCT t.ID)
|
||||||
|
{$this->sql()}
|
||||||
|
", $this->user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function idList(): array {
|
||||||
|
$sql = "
|
||||||
|
SELECT DISTINCT t.ID
|
||||||
|
{$this->sql()}";
|
||||||
|
|
||||||
|
$args = [$this->user->id];
|
||||||
|
if (isset($this->limit)) {
|
||||||
|
$sql .= "\nLIMIT ?";
|
||||||
|
$args[] = $this->limit;
|
||||||
|
}
|
||||||
|
if (isset($this->offset)) {
|
||||||
|
$sql .= "\nOFFSET ?";
|
||||||
|
$args[] = $this->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$db->prepared_query($sql, ...$args);
|
||||||
return self::$db->collect(0);
|
return self::$db->collect(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ exists for the sake of interoperability and may go away in the future.
|
|||||||
* [Upload](#upload)
|
* [Upload](#upload)
|
||||||
* [Download](#download)
|
* [Download](#download)
|
||||||
* [Add Log](#add-log)
|
* [Add Log](#add-log)
|
||||||
|
* [User Torrents](#user-torrents)
|
||||||
* [Better](#better)
|
* [Better](#better)
|
||||||
* [Logchecker](#logchecker)
|
* [Logchecker](#logchecker)
|
||||||
* [Requests](#requests)
|
* [Requests](#requests)
|
||||||
@@ -1538,6 +1539,63 @@ __Note__: Must be the uploader of the torrent or moderator to add logs
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### User Torrents
|
||||||
|
|
||||||
|
**URL:**
|
||||||
|
`ajax.php?action=user_torrents&userid=<User ID>&type=<Type>`
|
||||||
|
|
||||||
|
__Note__: Must be the uploader of the torrent or moderator to add logs
|
||||||
|
|
||||||
|
**GET Arguments:**
|
||||||
|
|
||||||
|
`userid` - The user id to get torrents for (can also use `id`)
|
||||||
|
`type` - Type of torrents to display (options are `downloaded`, `leeching`, `seeding`, `snatched`, `snatched-unseeded`, `uploaded`, `uploaded-unseeded`)
|
||||||
|
`limit` - (Optional) Number of results to display (default: 500)
|
||||||
|
`offset` - (Optional) Number of results to offset by (default: 0)
|
||||||
|
`page` - (Optional) Page of results to show (default: 1)
|
||||||
|
|
||||||
|
__NOTE__: You can only provide one of `offset` or `page`. Offset is a number of
|
||||||
|
rows to skip before starting to collect the result set, while page is the page
|
||||||
|
number of results to retrieve, where `(page - 1) * limit == offset`.
|
||||||
|
|
||||||
|
**Response format:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"response": {
|
||||||
|
"uploaded": [
|
||||||
|
{
|
||||||
|
"groupId": 1,
|
||||||
|
"torrentId": 1,
|
||||||
|
"name": "Foo",
|
||||||
|
"torrentSize": 12345,
|
||||||
|
"artistId": 1,
|
||||||
|
"artistName": "Bar",
|
||||||
|
"artists": {
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"aliasid": 1,
|
||||||
|
"name": "Bar",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"source": "Gazelle Dev",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
__NOTE__: For a torrent that has many main artists, `artistName` will be
|
||||||
|
"Various ..." and the `artistId` will be the ID of the first main artist. It is
|
||||||
|
recommended to use the `artists` field to get artist information.
|
||||||
|
|
||||||
## Better
|
## Better
|
||||||
|
|
||||||
**URL:**
|
**URL:**
|
||||||
|
|||||||
@@ -257,6 +257,9 @@ switch ($Action) {
|
|||||||
case 'add_log':
|
case 'add_log':
|
||||||
include_once __DIR__ . '/add_log.php';
|
include_once __DIR__ . '/add_log.php';
|
||||||
break;
|
break;
|
||||||
|
case 'user_torrents':
|
||||||
|
include_once __DIR__ . '/user_torrents.php';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// If they're screwing around with the query string
|
// If they're screwing around with the query string
|
||||||
json_error("failure");
|
json_error("failure");
|
||||||
|
|||||||
48
sections/ajax/user_torrents.php
Normal file
48
sections/ajax/user_torrents.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
/** @phpstan-var \Gazelle\User $Viewer */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Gazelle;
|
||||||
|
|
||||||
|
if (!isset($_GET['type']) || !in_array($_GET['type'], ['snatched', 'snatched-unseeded', 'seeding', 'leeching', 'uploaded', 'uploaded-unseeded', 'downloaded'])) {
|
||||||
|
json_error("bad type");
|
||||||
|
}
|
||||||
|
$type = (string)$_GET['type'];
|
||||||
|
|
||||||
|
foreach (['limit', 'offset', 'page'] as $key) {
|
||||||
|
if (isset($_GET[$key]) && !ctype_digit($_GET[$key])) {
|
||||||
|
json_error("non-numeric value for {$key}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['offset']) && isset($_GET['page'])) {
|
||||||
|
json_error('can only use one of offset or page');
|
||||||
|
}
|
||||||
|
if (isset($_GET['offset']) && (int)$_GET['offset'] < 0) {
|
||||||
|
json_error('invalid offset parameter, must be 0 or greater');
|
||||||
|
}
|
||||||
|
if (isset($_GET['page']) && (int)$_GET['page'] < 1) {
|
||||||
|
json_error('invalid page parameter, must be 1 or greater');
|
||||||
|
}
|
||||||
|
|
||||||
|
$limit = (int)($_GET['limit'] ?? 500);
|
||||||
|
$offset = isset($_GET['page']) ? (int)($_GET['page'] - 1) * $limit : (int)($_GET['offset'] ?? 0);
|
||||||
|
if ($limit < 1) {
|
||||||
|
json_error('invalid limit parameter, must be 1 or greater');
|
||||||
|
}
|
||||||
|
|
||||||
|
// We accept id to match RED, but userid is the better param name and matches user_recents
|
||||||
|
$user = new Manager\User()->findById((int)($_GET['userid'] ?? $_GET['id'] ?? 0));
|
||||||
|
if (is_null($user)) {
|
||||||
|
json_error("bad userid");
|
||||||
|
}
|
||||||
|
if (!$user->propertyVisible($Viewer, str_replace('-unseeded', '', $type))) {
|
||||||
|
json_error('user has hidden this');
|
||||||
|
}
|
||||||
|
|
||||||
|
echo new Json\UserTorrents($user, $Viewer)
|
||||||
|
->setType($type)
|
||||||
|
->setLimit($limit)
|
||||||
|
->setOffset($offset)
|
||||||
|
->response();
|
||||||
Reference in New Issue
Block a user