Merge pull request #2599 from pixelfed/staging

Staging
This commit is contained in:
daniel 2021-01-30 17:39:04 -07:00 committed by GitHub
commit 8757476aff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 195 additions and 66 deletions

View file

@ -115,7 +115,7 @@ PF_COSTAR_ENABLED=false
MEDIA_EXIF_DATABASE=false
## Logging
LOG_CHANNEL=stack
LOG_CHANNEL=stderr
## Image
IMAGE_DRIVER=imagick

View file

@ -6,7 +6,19 @@
### Updated
- Updated AdminController, fix variable name in updateSpam method. ([6edaf940](https://github.com/pixelfed/pixelfed/commit/6edaf940))
- Updated RemotAvatarFetch, only dispatch jobs if cloud storage is enabled. ([4f40f6f5](https://github.com/pixelfed/pixelfed/commit/4f40f6f5))
- Updated StatusService, add ttl of 7 days. ([6e44ae0b](https://github.com/pixelfed/pixelfed/commit/6e44ae0b))
- Updated StatusHashtagService, use StatusService for statuses. ([0355b567](https://github.com/pixelfed/pixelfed/commit/0355b567))
- Updated StatusHashtagService, remove deprecated methods. ([aa4c718d](https://github.com/pixelfed/pixelfed/commit/aa4c718d))
- Updated ApiV1Controller, add StatusService del calls to update likes_count, reblogs_count and reply_count. ([05b9445c](https://github.com/pixelfed/pixelfed/commit/05b9445c))
- Updated Like, Status and Comment controllers to add StatusService del() method to update counts. ([eab4370c](https://github.com/pixelfed/pixelfed/commit/eab4370c))
- Updated ComposeController, use placeholder image for video media. Fixes #2595. ([789ed4b4](https://github.com/pixelfed/pixelfed/commit/789ed4b4))
- Updated DiscoverController, change api schema. ([2eea0409](https://github.com/pixelfed/pixelfed/commit/2eea0409))
- Updated StatusDelete pipeline, call StatusService::del() to remove status from cache. ([3f772ff8](https://github.com/pixelfed/pixelfed/commit/3f772ff8))
- Updated StatusHashtagTransformer, add blurhash attribute. ([899bbeba](https://github.com/pixelfed/pixelfed/commit/899bbeba))
- Updated status square previews, add blurhash and improved content warnings. ([39e389dd](https://github.com/pixelfed/pixelfed/commit/39e389dd))
- Updated Blurhash util, add default hash for invalid media. ([38a37c15](https://github.com/pixelfed/pixelfed/commit/38a37c15))
- Updated VideoThumbnail job, generate blurhash for videos. ([896452c7](https://github.com/pixelfed/pixelfed/commit/896452c7))
- Updated MediaTransformers, add default blurhash attribute. ([3f14a4c4](https://github.com/pixelfed/pixelfed/commit/3f14a4c4))
## [v0.10.10 (2021-01-28)](https://github.com/pixelfed/pixelfed/compare/v0.10.9...v0.10.10)
### Added

View file

@ -50,6 +50,7 @@ use App\Services\{
NotificationService,
MediaPathService,
SearchApiV2Service,
StatusService,
MediaBlocklistService
};
@ -856,6 +857,8 @@ class ApiV1Controller extends Controller
$status->save();
}
StatusService::del($status->id);
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res);
@ -1766,6 +1769,7 @@ class ApiV1Controller extends Controller
$status->in_reply_to_id = $parent->id;
$status->in_reply_to_profile_id = $parent->profile_id;
$status->save();
StatusService::del($parent->id);
} else if($ids) {
if(Media::whereUserId($user->id)
->whereNull('status_id')
@ -1883,6 +1887,7 @@ class ApiV1Controller extends Controller
SharePipeline::dispatch($share);
}
StatusService::del($status->id);
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res);
@ -1916,6 +1921,7 @@ class ApiV1Controller extends Controller
$status->reblogs_count = $status->shares()->count();
$status->save();
StatusService::del($status->id);
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res);

View file

@ -18,6 +18,7 @@ use League\Fractal;
use App\Transformer\Api\StatusTransformer;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Services\StatusService;
class CommentController extends Controller
{
@ -78,6 +79,7 @@ class CommentController extends Controller
return $reply;
});
StatusService::del($status->id);
NewStatusPipeline::dispatch($reply, false);
CommentPipeline::dispatch($status, $reply);

View file

@ -39,6 +39,7 @@ use App\Services\NotificationService;
use App\Services\MediaPathService;
use App\Services\MediaBlocklistService;
use App\Services\MediaTagService;
use App\Services\ServiceService;
use Illuminate\Support\Str;
use App\Util\Lexer\Autolink;
use App\Util\Lexer\Extractor;
@ -117,9 +118,8 @@ class ComposeController extends Controller
$media->version = 3;
$media->save();
// $url = URL::temporarySignedRoute(
// 'temp-media', now()->addHours(1), ['profileId' => $profile->id, 'mediaId' => $media->id, 'timestamp' => time()]
// );
$preview_url = $media->url() . '?v=' . time();
$url = $media->url() . '?v=' . time();
switch ($media->mime) {
case 'image/jpeg':
@ -139,8 +139,8 @@ class ComposeController extends Controller
$resource = new Fractal\Resource\Item($media, new MediaTransformer());
$res = $this->fractal->createData($resource)->toArray();
$res['preview_url'] = $media->url() . '?v=' . time();
$res['url'] = $media->url() . '?v=' . time();
$res['preview_url'] = $preview_url;
$res['url'] = $url;
return response()->json($res);
}

View file

@ -129,10 +129,16 @@ class DiscoverController extends Controller
$tag = $request->input('hashtag');
$hashtag = Hashtag::whereName($tag)->firstOrFail();
$res['tags'] = StatusHashtagService::get($hashtag->id, $page, $end);
if($page == 1) {
$res['follows'] = HashtagFollow::whereUserId(Auth::id())->whereHashtagId($hashtag->id)->exists();
$res['follows'] = HashtagFollow::whereUserId(Auth::id())
->whereHashtagId($hashtag->id)
->exists();
}
$res['hashtag'] = [
'name' => $hashtag->name,
'url' => $hashtag->url()
];
$res['tags'] = StatusHashtagService::get($hashtag->id, $page, $end);
return $res;
}

View file

@ -9,6 +9,7 @@ use App\User;
use Auth;
use Cache;
use Illuminate\Http\Request;
use App\Services\StatusService;
class LikeController extends Controller
{
@ -58,6 +59,7 @@ class LikeController extends Controller
}
Cache::forget('status:'.$status->id.':likedby:userid:'.$user->id);
StatusService::del($status->id);
if ($request->ajax()) {
$response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count];

View file

@ -20,6 +20,7 @@ use League\Fractal;
use App\Util\Media\Filter;
use Illuminate\Support\Str;
use App\Services\HashidService;
use App\Services\StatusService;
class StatusController extends Controller
{
@ -211,6 +212,7 @@ class StatusController extends Controller
Cache::forget('_api:statuses:recent_9:' . $status->profile_id);
Cache::forget('profile:status_count:' . $status->profile_id);
StatusService::del($status->id);
if ($status->profile_id == $user->profile->id || $user->is_admin == true) {
Cache::forget('profile:status_count:'.$status->profile_id);
StatusDelete::dispatch($status);
@ -266,6 +268,7 @@ class StatusController extends Controller
}
Cache::forget('status:'.$status->id.':sharedby:userid:'.$user->id);
StatusService::del($status->id);
if ($request->ajax()) {
$response = ['code' => 200, 'msg' => 'Share saved', 'count' => $count];

View file

@ -14,6 +14,7 @@ use App\Util\ActivityPub\Helpers;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use App\Transformer\ActivityPub\Verb\Like as LikeTransformer;
use App\Services\StatusService;
class LikePipeline implements ShouldQueue
{
@ -58,6 +59,8 @@ class LikePipeline implements ShouldQueue
return;
}
StatusService::del($status->id);
if($status->url && $actor->domain == null) {
return $this->remoteLikeDeliver();
}

View file

@ -25,6 +25,7 @@ use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
use App\Util\ActivityPub\HttpSignature;
use App\Services\StatusService;
class StatusDelete implements ShouldQueue
{
@ -59,6 +60,7 @@ class StatusDelete implements ShouldQueue
$status = $this->status;
$profile = $this->status->profile;
StatusService::del($status->id);
$count = $profile->statuses()
->getQuery()
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])

View file

@ -13,6 +13,7 @@ use FFMpeg;
use Storage;
use App\Media;
use App\Jobs\MediaPipeline\MediaStoragePipeline;
use App\Util\Media\Blurhash;
class VideoThumbnail implements ShouldQueue
{
@ -59,6 +60,12 @@ class VideoThumbnail implements ShouldQueue
$media->thumbnail_path = $save;
$media->save();
$blurhash = Blurhash::generate($media);
if($blurhash) {
$media->blurhash = $blurhash;
$media->save();
}
} catch (Exception $e) {
}

View file

@ -16,6 +16,10 @@ class StatusHashtagService {
public static function get($id, $page = 1, $stop = 9)
{
if($page > 20) {
return [];
}
return StatusHashtag::whereHashtagId($id)
->whereStatusVisibility('public')
->whereHas('media')
@ -47,12 +51,12 @@ class StatusHashtagService {
public static function set($key, $val)
{
return Redis::zadd(self::CACHE_KEY . $key, $val, $val);
return 1;
}
public static function del($key)
{
return Redis::zrem(self::CACHE_KEY . $key, $key);
return 1;
}
public static function count($id)
@ -66,16 +70,6 @@ class StatusHashtagService {
public static function getStatus($statusId, $hashtagId)
{
return Cache::remember('pf:services:status-hashtag:post:'.$statusId.':hashtag:'.$hashtagId, now()->addMonths(3), function() use($statusId, $hashtagId) {
$statusHashtag = StatusHashtag::with('profile', 'status', 'hashtag')
->whereStatusVisibility('public')
->whereStatusId($statusId)
->whereHashtagId($hashtagId)
->first();
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($statusHashtag, new StatusHashtagTransformer());
return $fractal->createData($resource)->toArray();
});
return ['status' => StatusService::get($statusId)];
}
}

View file

@ -2,6 +2,7 @@
namespace App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
use App\Status;
//use App\Transformer\Api\v3\StatusTransformer;
@ -15,34 +16,27 @@ class StatusService {
const CACHE_KEY = 'pf:services:status:';
public static function get($id)
public static function key($id)
{
return json_decode(Redis::get(self::CACHE_KEY . $id) ?? self::coldGet($id), true);
return self::CACHE_KEY . $id;
}
public static function coldGet($id)
public static function get($id)
{
$status = Status::whereScope('public')->findOrFail($id);
return Cache::remember(self::key($id), now()->addDays(7), function() use($id) {
$status = Status::whereScope('public')->find($id);
if(!$status) {
return null;
}
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($status, new StatusStatelessTransformer());
$res = $fractal->createData($resource)->toJson();
self::set($id, $res);
return $res;
return $fractal->createData($resource)->toArray();
});
}
public static function set($key, $val)
public static function del($id)
{
return Redis::set(self::CACHE_KEY . $key, $val);
}
public static function del($key)
{
return Redis::del(self::CACHE_KEY . $key);
}
public static function rem($key)
{
return self::del($key);
return Cache::forget(self::key($id));
}
}

View file

@ -18,7 +18,7 @@ class MediaTransformer extends Fractal\TransformerAbstract
'text_url' => null,
'meta' => null,
'description' => $media->caption,
'blurhash' => $media->blurhash
'blurhash' => $media->blurhash ?? 'U4Rfzst8?bt7ogayj[j[~pfQ9Goe%Mj[WBay'
];
if($media->width && $media->height) {

View file

@ -24,7 +24,7 @@ class MediaTransformer extends Fractal\TransformerAbstract
'filter_name' => $media->filter_name,
'filter_class' => $media->version == 1 ? $media->filter_class : null,
'mime' => $media->mime,
'blurhash' => $media->blurhash
'blurhash' => $media->blurhash ?? 'U4Rfzst8?bt7ogayj[j[~pfQ9Goe%Mj[WBay'
];
if($media->width && $media->height) {

View file

@ -20,6 +20,7 @@ class StatusHashtagTransformer extends Fractal\TransformerAbstract
'url' => $status->url(),
'thumb' => $status->thumb(true),
'filter' => $status->firstMedia()->filter_class,
'blurhash' => $status->firstMedia()->blurhash,
'sensitive' => (bool) $status->is_nsfw,
'like_count' => $status->likes_count,
'share_count' => $status->reblogs_count,

View file

@ -20,6 +20,7 @@ class StatusStatelessTransformer extends Fractal\TransformerAbstract
$taggedPeople = MediaTagService::get($status->id);
return [
'_v' => 1,
'id' => (string) $status->id,
'shortcode' => HashidService::encode($status->id),
'uri' => $status->url(),

View file

@ -22,6 +22,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
$taggedPeople = MediaTagService::get($status->id);
return [
'_v' => 1,
'id' => (string) $status->id,
'shortcode' => HashidService::encode($status->id),
'uri' => $status->url(),

View file

@ -7,19 +7,28 @@ use App\Media;
class Blurhash {
const DEFAULT_HASH = 'U4Rfzst8?bt7ogayj[j[~pfQ9Goe%Mj[WBay';
public static function generate(Media $media)
{
if(!in_array($media->mime, ['image/png', 'image/jpeg'])) {
return;
if(!in_array($media->mime, ['image/png', 'image/jpeg', 'video/mp4'])) {
return self::DEFAULT_HASH;
}
if($media->thumbnail_path == null) {
return self::DEFAULT_HASH;
}
$file = storage_path('app/' . $media->thumbnail_path);
if(!is_file($file)) {
return;
return self::DEFAULT_HASH;
}
$image = imagecreatefromstring(file_get_contents($file));
if(!$image) {
return self::DEFAULT_HASH;
}
$width = imagesx($image);
$height = imagesy($image);
@ -39,7 +48,7 @@ class Blurhash {
$components_y = 4;
$blurhash = BlurhashEngine::encode($pixels, $components_x, $components_y);
if(strlen($blurhash) > 191) {
return;
return self::DEFAULT_HASH;
}
return $blurhash;
}

BIN
public/css/app.css vendored

Binary file not shown.

BIN
public/css/appdark.css vendored

Binary file not shown.

BIN
public/css/landing.css vendored

Binary file not shown.

BIN
public/js/hashtag.js vendored

Binary file not shown.

BIN
public/js/profile.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -36,8 +36,16 @@
<div v-for="(tag, index) in top" class="col-4 p-0 p-sm-2 p-md-3 hashtag-post-square">
<a class="card info-overlay card-md-border-0" :href="tag.status.url">
<div :class="[tag.status.filter ? 'square ' + tag.status.filter : 'square']">
<div v-if="tag.status.sensitive && forceNsfw == false" class="square-content" :style="'background-image: url(/storage/no-preview.png)'"></div>
<div v-else class="square-content" :style="'background-image: url('+tag.status.thumb+')'"></div>
<div v-if="tag.status.sensitive && forceNsfw == false" class="square-content">
<blur-hash-image
v-if="s.sensitive"
width="32"
height="32"
punch="1"
:hash="tag.status.media_attachments[0].blurhash"
/>
</div>
<div v-else class="square-content" :style="'background-image: url('+tag.status.media_attachments[0].preview_url+')'"></div>
<div class="info-overlay-text">
<h5 class="text-white m-auto font-weight-bold">
<span class="pr-4">
@ -57,15 +65,38 @@
<div v-for="(tag, index) in tags" class="col-4 p-0 p-sm-2 p-md-3 hashtag-post-square">
<a class="card info-overlay card-md-border-0" :href="tag.status.url">
<div :class="[tag.status.filter ? 'square ' + tag.status.filter : 'square']">
<div v-if="tag.status.sensitive && forceNsfw == false" class="square-content" :style="'background-image: url(/storage/no-preview.png)'"></div>
<div v-else class="square-content" :style="'background-image: url('+tag.status.thumb+')'"></div>
<div v-if="tag.status.sensitive && forceNsfw == false" class="square-content">
<div class="info-overlay-text-label">
<h5 class="text-white m-auto font-weight-bold">
<span>
<span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span>
</span>
</h5>
</div>
<blur-hash-canvas
width="32"
height="32"
:hash="tag.status.media_attachments[0].blurhash"
/>
</div>
<div v-else class="square-content">
<blur-hash-image
width="32"
height="32"
:hash="tag.status.media_attachments[0].blurhash"
:src="tag.status.media_attachments[0].preview_url"
/>
</div>
<span v-if="tag.status.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span>
<span v-if="tag.status.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
<span v-if="tag.status.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
<div class="info-overlay-text">
<h5 class="text-white m-auto font-weight-bold">
<span class="pr-4">
<span class="far fa-heart fa-lg pr-1"></span> {{tag.status.like_count}}
<span class="far fa-heart fa-lg pr-1"></span> {{tag.status.favourites_count}}
</span>
<span>
<span class="fas fa-retweet fa-lg pr-1"></span> {{tag.status.share_count}}
<span class="far fa-comment fa-lg pr-1"></span> {{tag.status.reply_count}}
</span>
</h5>
</div>

View file

@ -183,21 +183,42 @@
<div class="row" v-if="mode == 'grid'">
<div class="col-4 p-1 p-md-3" v-for="(s, index) in timeline" :key="'tlob:'+index">
<a class="card info-overlay card-md-border-0" :href="statusUrl(s)" v-once>
<div :class="[s.sensitive ? 'square' : 'square ' + s.media_attachments[0].filter_class]">
<div class="square">
<div v-if="s.sensitive" class="square-content">
<div class="info-overlay-text-label">
<h5 class="text-white m-auto font-weight-bold">
<span>
<span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span>
</span>
</h5>
</div>
<blur-hash-canvas
width="32"
height="32"
:hash="s.media_attachments[0].blurhash"
/>
</div>
<div v-else class="square-content">
<blur-hash-image
width="32"
height="32"
:hash="s.media_attachments[0].blurhash"
:src="s.media_attachments[0].preview_url"
/>
</div>
<span v-if="s.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span>
<span v-if="s.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
<span v-if="s.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
<div class="square-content" v-bind:style="previewBackground(s)">
</div>
<div class="info-overlay-text">
<h5 class="text-white m-auto font-weight-bold">
<span>
<span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{s.favourites_count}}</span>
<span class="d-flex-inline">{{formatCount(s.favourites_count)}}</span>
</span>
<span>
<span class="fas fa-retweet fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{s.reblogs_count}}</span>
<span class="far fa-comment fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{formatCount(s.reply_count)}}</span>
</span>
</h5>
</div>
@ -818,6 +839,11 @@
return 'background-image: url(' + preview + ');';
},
blurhHashMedia(status) {
return status.sensitive ? null :
status.media_attachments[0].preview_url;
},
switchMode(mode) {
this.mode = _.indexOf(this.modes, mode) ? mode : 'grid';
if(this.mode == 'bookmarks' && this.bookmarks.length == 0) {

View file

@ -131,6 +131,22 @@ body, button, input, textarea {
background-color: rgba(0,0,0,0.5);
}
.info-overlay-text-label {
display: flex;
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
h5 {
z-index: 2;
}
}
.info-overlay:hover .info-overlay-text-label {
display: none;
}
.font-weight-lighter {
font-weight: 300 !important
}
@ -566,3 +582,16 @@ details summary::-webkit-details-marker {
.follow-modal {
max-width: 400px !important;
}
.square-content {
img {
object-fit: cover !important;
}
}
.square .square-content {
canvas {
width: 100%;
height: 100%;
}
}