mirror of
https://github.com/HDInnovations/UNIT3D-Community-Edition.git
synced 2026-02-09 13:14:07 -06:00
1. Need to use the path of a file and not the object. 2. When a torrent is uploaded, the upload form sends a string for the tmdb and the model is saved with a string and it's the database that does the conversion. We need to cast the string to an int if we want to reuse the model without rehydrating. 3. Some of the first_air_date and last_air_date are null and need to be checked to make sure they follow the correct format.
470 lines
16 KiB
PHP
470 lines
16 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* NOTICE OF LICENSE.
|
|
*
|
|
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
|
* The details is bundled with this project in the file LICENSE.txt.
|
|
*
|
|
* @project UNIT3D Community Edition
|
|
*
|
|
* @author HDVinnie <hdinnovations@protonmail.com>
|
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
|
*/
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Helpers\Bbcode;
|
|
use App\Helpers\Linkify;
|
|
use App\Helpers\MediaInfo;
|
|
use App\Helpers\StringHelper;
|
|
use App\Models\Scopes\ApprovedScope;
|
|
use App\Notifications\NewComment;
|
|
use App\Notifications\NewThank;
|
|
use App\Traits\Auditable;
|
|
use App\Traits\GroupedLastScope;
|
|
use App\Traits\TorrentFilter;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use voku\helper\AntiXSS;
|
|
|
|
/**
|
|
* App\Models\Torrent.
|
|
*
|
|
* @property string $info_hash
|
|
* @property int $id
|
|
* @property string $name
|
|
* @property string $description
|
|
* @property string|null $mediainfo
|
|
* @property string|null $bdinfo
|
|
* @property string $file_name
|
|
* @property int $num_file
|
|
* @property string|null $folder
|
|
* @property float $size
|
|
* @property mixed|null $nfo
|
|
* @property int $leechers
|
|
* @property int $seeders
|
|
* @property int $times_completed
|
|
* @property int|null $category_id
|
|
* @property int $user_id
|
|
* @property int $imdb
|
|
* @property int $tvdb
|
|
* @property int $tmdb
|
|
* @property int $mal
|
|
* @property int $igdb
|
|
* @property int|null $season_number
|
|
* @property int|null $episode_number
|
|
* @property int $stream
|
|
* @property int $free
|
|
* @property bool $doubleup
|
|
* @property bool $refundable
|
|
* @property int $highspeed
|
|
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\FeaturedTorrent> $featured
|
|
* @property int $status
|
|
* @property \Illuminate\Support\Carbon|null $moderated_at
|
|
* @property int|null $moderated_by
|
|
* @property int $anon
|
|
* @property bool $sticky
|
|
* @property int $sd
|
|
* @property int $internal
|
|
* @property \Illuminate\Support\Carbon|null $created_at
|
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
|
* @property \Illuminate\Support\Carbon|null $bumped_at
|
|
* @property \Illuminate\Support\Carbon|null $fl_until
|
|
* @property \Illuminate\Support\Carbon|null $du_until
|
|
* @property string|null $release_year
|
|
* @property int $type_id
|
|
* @property int|null $resolution_id
|
|
* @property int|null $distributor_id
|
|
* @property int|null $region_id
|
|
* @property int $personal_release
|
|
* @property int|null $balance
|
|
* @property int|null $balance_offset
|
|
*/
|
|
class Torrent extends Model
|
|
{
|
|
use Auditable;
|
|
use GroupedLastScope;
|
|
use HasFactory;
|
|
use TorrentFilter;
|
|
|
|
protected $guarded = [];
|
|
|
|
/**
|
|
* Get the attributes that should be cast.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'tmdb' => 'integer',
|
|
'igdb' => 'integer',
|
|
'bumped_at' => 'datetime',
|
|
'fl_until' => 'datetime',
|
|
'du_until' => 'datetime',
|
|
'doubleup' => 'boolean',
|
|
'refundable' => 'boolean',
|
|
'featured' => 'boolean',
|
|
'moderated_at' => 'datetime',
|
|
'sticky' => 'boolean',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* The attributes that should not be included in audit log.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
protected $discarded = [
|
|
'info_hash',
|
|
];
|
|
|
|
final public const PENDING = 0;
|
|
final public const APPROVED = 1;
|
|
final public const REJECTED = 2;
|
|
final public const POSTPONED = 3;
|
|
|
|
protected static function booted(): void
|
|
{
|
|
static::addGlobalScope(new ApprovedScope());
|
|
}
|
|
|
|
/**
|
|
* Belongs To A User.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<User, self>
|
|
*/
|
|
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class)->withDefault([
|
|
'username' => 'System',
|
|
'id' => '1',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Category.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Category, self>
|
|
*/
|
|
public function category(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Category::class);
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Type.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Type, self>
|
|
*/
|
|
public function type(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Type::class);
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Resolution.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Resolution, self>
|
|
*/
|
|
public function resolution(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Resolution::class);
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Distributor.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Distributor, self>
|
|
*/
|
|
public function distributor(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Distributor::class);
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Region.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Region, self>
|
|
*/
|
|
public function region(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Region::class);
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Movie.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Movie, self>
|
|
*/
|
|
public function movie(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Movie::class, 'tmdb');
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Tv.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<Tv, self>
|
|
*/
|
|
public function tv(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Tv::class, 'tmdb');
|
|
}
|
|
|
|
/**
|
|
* Belongs To A Playlist.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<Playlist>
|
|
*/
|
|
public function playlists(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
{
|
|
return $this->belongsToMany(Playlist::class, 'playlist_torrents')->using(PlaylistTorrent::class)->withPivot('id');
|
|
}
|
|
|
|
/**
|
|
* Torrent Has Been Moderated By.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<User, self>
|
|
*/
|
|
public function moderated(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'moderated_by')->withDefault([
|
|
'username' => 'System',
|
|
'id' => '1',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Has Many Keywords.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Keyword>
|
|
*/
|
|
public function keywords(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Keyword::class);
|
|
}
|
|
|
|
/**
|
|
* Has Many History.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<History>
|
|
*/
|
|
public function history(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(History::class);
|
|
}
|
|
|
|
/**
|
|
* Has Many Tips.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<TorrentTip>
|
|
*/
|
|
public function tips(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(TorrentTip::class);
|
|
}
|
|
|
|
/**
|
|
* Has Many Thank.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Thank>
|
|
*/
|
|
public function thanks(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Thank::class);
|
|
}
|
|
|
|
/**
|
|
* Has Many HitRuns.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Warning>
|
|
*/
|
|
public function hitrun(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Warning::class, 'torrent');
|
|
}
|
|
|
|
/**
|
|
* Has Many Featured.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<FeaturedTorrent>
|
|
*/
|
|
public function featured(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(FeaturedTorrent::class);
|
|
}
|
|
|
|
/**
|
|
* Has Many Files.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<TorrentFile>
|
|
*/
|
|
public function files(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(TorrentFile::class);
|
|
}
|
|
|
|
/**
|
|
* @return \Illuminate\Database\Eloquent\Relations\MorphMany<Comment>
|
|
*/
|
|
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
|
|
{
|
|
return $this->morphMany(Comment::class, 'commentable');
|
|
}
|
|
|
|
/**
|
|
* Has Many Peers.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Peer>
|
|
*/
|
|
public function peers(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Peer::class);
|
|
}
|
|
|
|
/**
|
|
* Has Many Seeds.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Peer>
|
|
*/
|
|
public function seeds(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Peer::class)->where('seeder', '=', true);
|
|
}
|
|
|
|
/**
|
|
* Has Many Leeches.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Peer>
|
|
*/
|
|
public function leeches(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Peer::class)->where('seeder', '=', false);
|
|
}
|
|
|
|
/**
|
|
* Has Many Subtitles.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Subtitle>
|
|
*/
|
|
public function subtitles(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Subtitle::class);
|
|
}
|
|
|
|
/**
|
|
* Relationship To Many Requests.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<TorrentRequest>
|
|
*/
|
|
public function requests(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(TorrentRequest::class);
|
|
}
|
|
|
|
/**
|
|
* Has many free leech tokens.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<FreeleechToken>
|
|
*/
|
|
public function freeleechTokens(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(FreeleechToken::class);
|
|
}
|
|
|
|
/**
|
|
* Bookmarks.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Bookmark>
|
|
*/
|
|
public function bookmarks(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Bookmark::class);
|
|
}
|
|
|
|
/**
|
|
* Bookmarks.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<Resurrection>
|
|
*/
|
|
public function resurrections(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Resurrection::class);
|
|
}
|
|
|
|
/**
|
|
* Set The Torrents Description After Its Been Purified.
|
|
*/
|
|
public function setDescriptionAttribute(?string $value): void
|
|
{
|
|
$this->attributes['description'] = $value === null ? null : htmlspecialchars((new AntiXSS())->xss_clean($value), ENT_NOQUOTES);
|
|
}
|
|
|
|
/**
|
|
* Parse Description And Return Valid HTML.
|
|
*/
|
|
public function getDescriptionHtml(): string
|
|
{
|
|
$bbcode = new Bbcode();
|
|
|
|
return (new Linkify())->linky($bbcode->parse($this->description));
|
|
}
|
|
|
|
/**
|
|
* Set The Torrents MediaInfo After Its Been Purified.
|
|
*/
|
|
public function setMediaInfoAttribute(?string $value): void
|
|
{
|
|
$this->attributes['mediainfo'] = $value;
|
|
}
|
|
|
|
/**
|
|
* Returns The Size In Human Format.
|
|
*/
|
|
public function getSize(): string
|
|
{
|
|
$bytes = $this->size;
|
|
|
|
return StringHelper::formatBytes($bytes, 2);
|
|
}
|
|
|
|
/**
|
|
* Notify Uploader When An Action Is Taken.
|
|
*/
|
|
public function notifyUploader(string $type, Thank|Comment $payload): bool
|
|
{
|
|
$user = User::with('notification')->findOrFail($this->user_id);
|
|
|
|
switch (true) {
|
|
case $payload instanceof Thank:
|
|
if ($user->acceptsNotification(auth()->user(), $user, 'torrent', 'show_torrent_thank')) {
|
|
$user->notify(new NewThank('torrent', $payload));
|
|
}
|
|
|
|
break;
|
|
case $payload instanceof Comment:
|
|
if ($user->acceptsNotification(auth()->user(), $user, 'torrent', 'show_torrent_comment')) {
|
|
$user->notify(new NewComment('torrent', $payload));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Torrent Is Freeleech.
|
|
*/
|
|
public function isFreeleech(User $user = null): bool
|
|
{
|
|
$pfree = $user && ($user->group->is_freeleech || cache()->get('personal_freeleech:'.$user->id));
|
|
|
|
return $this->free || config('other.freeleech') || $pfree;
|
|
}
|
|
}
|