Biggus Upgradus

- !! NEW DOCKER FILES :D !!
- getid3 is now vendored from composer! :D
- fix elasticsearch for use with newer versions
- fix some migration issues by yeeting a migration that has had its day
- fix our asset pipeline (webpack / gulp)
This commit is contained in:
Adam Lavin 2021-03-27 03:51:45 +00:00
parent 7de9c6c7e7
commit ff57ce54dd
No known key found for this signature in database
GPG key ID: 9C4C68AFA9CA6461
41 changed files with 2864 additions and 1507 deletions

1
.gitignore vendored
View file

@ -13,3 +13,4 @@ resources/views/emails/html
npm-debug.log
yarn-error.log
/composer.phar
.phpstorm.meta.php

View file

@ -20,13 +20,13 @@
namespace App\Commands;
use App\Facades\Notification;
use App\Models\Album;
use App\Models\Comment;
use App\Models\Playlist;
use App\Models\Track;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Validator;
class CreateCommentCommand extends CommandBase

View file

@ -20,9 +20,9 @@
namespace App\Commands;
use App\Facades\Notification;
use App\Models\Playlist;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Validator;
class CreatePlaylistCommand extends CommandBase

View file

@ -20,6 +20,7 @@
namespace App\Commands;
use App\Facades\Notification;
use App\Models\Album;
use App\Models\Image;
use App\Models\Playlist;
@ -28,7 +29,6 @@ use App\Models\TrackType;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Notification;
class EditTrackCommand extends CommandBase
{

View file

@ -21,6 +21,7 @@
namespace App\Commands;
use App\Contracts\Favouritable;
use App\Facades\Notification;
use App\Models\Album;
use App\Models\Favourite;
use App\Models\Playlist;
@ -28,7 +29,6 @@ use App\Models\ResourceUser;
use App\Models\Track;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
class ToggleFavouriteCommand extends CommandBase
{

View file

@ -20,10 +20,10 @@
namespace App\Commands;
use App\Facades\Notification;
use App\Models\Follower;
use App\Models\ResourceUser;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Notification;
class ToggleFollowingCommand extends CommandBase
{

View file

@ -22,13 +22,11 @@ namespace App\Commands;
use App\Models\Track;
use App\Models\User;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Validator;
class UploadTrackCommand extends CommandBase
{
@ -161,7 +159,7 @@ class UploadTrackCommand extends CommandBase
.'audio_channels:1,2',
];
if (! $this->_isReplacingTrack) {
array_merge($rules, [
$rules = array_merge($rules, [
'cover' => 'image|mimes:png,jpeg|min_width:350|min_height:350',
'auto_publish' => 'boolean',
'title' => 'string',
@ -169,7 +167,7 @@ class UploadTrackCommand extends CommandBase
'genre' => 'string',
'album' => 'string',
'track_number' => 'integer',
'released_at' => 'date_format:'.Carbon::ISO8601,
'released_at' => 'date_format:'.CarbonInterface::ISO8601,
'description' => 'string',
'lyrics' => 'string',
'is_vocal' => 'boolean',

View file

@ -28,14 +28,12 @@ interface Searchable
*
* @return array
*/
public function toElasticsearch():array;
public function toElasticsearch(): array;
/**
* @return bool whether this particular object should be indexed or not
*/
public function shouldBeIndexed():bool;
public function shouldBeIndexed(): bool;
public function updateElasticsearchEntry();
public function updateElasticsearchEntrySynchronously();
public function updateElasticsearchEntry(bool $removeFromIndex = false);
}

View file

@ -43,7 +43,7 @@ class AlbumsController extends Controller
}
if ($album->slug != $slug) {
return Redirect::action('AlbumsController@getAlbum', [$id, $album->slug]);
return Redirect::action([AlbumsController::class, 'getAlbum'], [$id, $album->slug]);
}
return view('albums.show');
@ -56,7 +56,7 @@ class AlbumsController extends Controller
abort(404);
}
return Redirect::action('AlbumsController@getShow', [$id, $album->slug]);
return Redirect::action([AlbumsController::class, 'getShow'], [$id, $album->slug]);
}
public function getDownload($id, $extension)

View file

@ -22,6 +22,7 @@ namespace App\Http\Controllers\Api\V1;
use App\Commands\UploadTrackCommand;
use App\Http\Controllers\ApiControllerBase;
use App\Http\Controllers\ContentController;
use App\Models\Image;
use App\Models\Track;
use Illuminate\Support\Facades\Response;
@ -41,8 +42,8 @@ class TracksController extends ApiControllerBase
$data = [
'id' => (string) $commandData['id'],
'status_url' => action('Api\V1\TracksController@getUploadStatus', ['id' => $commandData['id']]),
'track_url' => action('TracksController@getTrack', ['id' => $commandData['id'], 'slug' => $commandData['slug']]),
'status_url' => action([static::class, 'getUploadStatus'], ['id' => $commandData['id']]),
'track_url' => action([\App\Http\Controllers\TracksController::class, 'getTrack'], ['id' => $commandData['id'], 'slug' => $commandData['slug']]),
'message' => $commandData['autoPublish']
? 'This track has been accepted for processing! Poll the status_url to know when it has been published. It will be published at the track_url.'
: "This track has been accepted for processing! Poll the status_url to know when it's ready to publish. It will be published at the track_url.",
@ -66,7 +67,7 @@ class TracksController extends ApiControllerBase
'message' => $track->published_at
? 'Processing complete! The track is live at the track_url. The artist can edit the track by visiting its edit_url.'
: 'Processing complete! The artist must publish the track by visiting its edit_url.',
'edit_url' => action('ContentController@getTracks', ['id' => $trackId]),
'edit_url' => action([ContentController::class, 'getTracks'], ['id' => $trackId]),
'track_url' => $track->url,
], 201);
} else {

View file

@ -33,8 +33,7 @@ use App\Models\Track;
use App\Models\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Request as RequestF;
class PlaylistsController extends ApiControllerBase
{
@ -222,8 +221,8 @@ class PlaylistsController extends ApiControllerBase
*/
private function applyOrdering($query)
{
if (Request::has('order')) {
$order = \Request::get('order');
if (RequestF::has('order')) {
$order = RequestF::get('order');
$parts = explode(',', $order);
$query->orderBy($parts[0], $parts[1]);
}

View file

@ -34,6 +34,7 @@ use App\Models\TrackType;
use App\Models\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Request as RequestF;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Response;
@ -295,8 +296,8 @@ class TracksController extends ApiControllerBase
*/
private function applyOrdering($query)
{
if (Request::has('order')) {
$order = \Request::get('order');
if (RequestF::has('order')) {
$order = RequestF::get('order');
$parts = explode(',', $order);
$query->orderBy($parts[0], $parts[1]);
}
@ -312,8 +313,8 @@ class TracksController extends ApiControllerBase
*/
private function applyFilters($query, $unknown = false)
{
if (Request::has('is_vocal')) {
$isVocal = \Request::get('is_vocal');
if (RequestF::has('is_vocal')) {
$isVocal = RequestF::get('is_vocal');
if ($isVocal == 'true') {
$query->whereIsVocal(true);
} else {
@ -321,27 +322,27 @@ class TracksController extends ApiControllerBase
}
}
if (Request::has('in_album')) {
if (Request::get('in_album') == 'true') {
if (RequestF::has('in_album')) {
if (RequestF::get('in_album') == 'true') {
$query->whereNotNull('album_id');
} else {
$query->whereNull('album_id');
}
}
if (Request::has('genres')) {
$query->whereIn('genre_id', Request::get('genres'));
if (RequestF::has('genres')) {
$query->whereIn('genre_id', RequestF::get('genres'));
}
if (Request::has('types') && ! $unknown) {
$query->whereIn('track_type_id', Request::get('types'));
if (RequestF::has('types') && ! $unknown) {
$query->whereIn('track_type_id', RequestF::get('types'));
}
$archive = null;
if (Request::has('archive')) {
if (RequestF::has('archive')) {
// Select which archive to view
$archive = Request::get('archive');
$archive = RequestF::get('archive');
$query->where('source', $archive);
}
@ -364,14 +365,14 @@ class TracksController extends ApiControllerBase
}
}
if (Request::has('songs')) {
if (RequestF::has('songs')) {
// DISTINCT is needed here to avoid duplicate results
// when a track is associated with multiple show songs.
$query->distinct();
$query->join('show_song_track', function ($join) {
$join->on('tracks.id', '=', 'show_song_track.track_id');
});
$query->whereIn('show_song_track.show_song_id', Request::get('songs'));
$query->whereIn('show_song_track.show_song_id', RequestF::get('songs'));
}
return $query;

View file

@ -51,7 +51,7 @@ class ArtistsController extends Controller
$newUser = User::find($user->redirect_to);
if ($newUser) {
return Redirect::action('ArtistsController@getProfile', [$newUser->slug]);
return Redirect::action([static::class, 'getProfile'], [$newUser->slug]);
}
}
@ -72,6 +72,6 @@ class ArtistsController extends Controller
abort('404');
}
return Redirect::action('ArtistsController@getProfile', [$user->slug]);
return Redirect::action([static::class, 'getProfile'], [$user->slug]);
}
}

View file

@ -46,7 +46,7 @@ class AuthController extends Controller
if (Auth::guest()) {
return redirect(
$this->poniverse
->getOAuthProvider(['redirectUri' => action('AuthController@getOAuth')])
->getOAuthProvider(['redirectUri' => action([static::class, 'getOAuth'])])
->getAuthorizationUrl());
}
@ -67,7 +67,7 @@ class AuthController extends Controller
try {
$accessToken = $oauthProvider->getAccessToken('authorization_code', [
'code' => $request->query('code'),
'redirect_uri' => action('AuthController@getOAuth'),
'redirect_uri' => action([static::class, 'getOAuth']),
]);
$this->poniverse->setAccessToken($accessToken);
$resourceOwner = $oauthProvider->getResourceOwner($accessToken);

View file

@ -45,7 +45,7 @@ class PlaylistsController extends Controller
}
if ($playlist->slug != $slug) {
return Redirect::action('PlaylistsController@getPlaylist', [$id, $playlist->slug]);
return Redirect::action([static::class, 'getPlaylist'], [$id, $playlist->slug]);
}
return view('playlists.show');
@ -58,7 +58,7 @@ class PlaylistsController extends Controller
abort(404);
}
return Redirect::action('PlaylistsController@getPlaylist', [$id, $playlist->slug]);
return Redirect::action([static::class, 'getPlaylist'], [$id, $playlist->slug]);
}
public function getDownload(Request $request, $id, $extension)

View file

@ -107,7 +107,7 @@ class TracksController extends Controller
'title' => $track->title,
'author_name' => $track->user->display_name,
'author_url' => $track->user->url,
'html' => '<iframe src="'.action('TracksController@getEmbed', ['id' => $track->id]).'" width="100%" height="150" allowTransparency="true" frameborder="0" seamless allowfullscreen></iframe>',
'html' => '<iframe src="'.action([static::class, 'getEmbed'], ['id' => $track->id]).'" width="100%" height="150" allowTransparency="true" frameborder="0" seamless allowfullscreen></iframe>',
];
return response()->json($output);
@ -121,7 +121,7 @@ class TracksController extends Controller
}
if ($track->slug != $slug) {
return Redirect::action('TracksController@getTrack', [$id, $track->slug]);
return Redirect::action([static::class, 'getTrack'], [$id, $track->slug]);
}
return view('tracks.show', ['track' => $track]);
@ -139,7 +139,7 @@ class TracksController extends Controller
abort(404);
}
return Redirect::action('TracksController@getTrack', [$id, $track->slug]);
return Redirect::action([static::class, 'getTrack'], [$id, $track->slug]);
}
public function getStream(Request $request, $id, $extension)

View file

@ -31,7 +31,6 @@ class Kernel extends HttpKernel
*/
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
@ -60,6 +59,7 @@ class Kernel extends HttpKernel
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'cors' => \Fruitcake\Cors\HandleCors::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,

View file

@ -130,7 +130,7 @@ class EncodeTrackFile extends Job implements ShouldQueue
Log::info('Encoding track file '.$this->trackFile->id.' into '.$target);
// Start a synchronous process to encode the file
$process = new Process($command);
$process = Process::fromShellCommandline($command);
try {
$process->mustRun();
} catch (ProcessFailedException $e) {

View file

@ -20,28 +20,28 @@
namespace App\Jobs;
use App\Contracts\Searchable;
use App\Jobs\Job;
use Elasticsearch;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\DB;
use SerializesModels;
class UpdateSearchIndexForEntity extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
use InteractsWithQueue;
protected $entity;
protected array $elasticsearchBody;
protected bool $removeFromIndex;
/**
* Create a new job instance.
*
* @param Model $entity
* @param array $elasticsearchBody
* @param bool $removeFromIndex
*/
public function __construct(Searchable $entity)
public function __construct(array $elasticsearchBody, bool $removeFromIndex = true)
{
$this->entity = $entity;
$this->elasticsearchBody = $elasticsearchBody;
$this->removeFromIndex = $removeFromIndex;
}
/**
@ -52,6 +52,26 @@ class UpdateSearchIndexForEntity extends Job implements ShouldQueue
public function handle()
{
$this->beforeHandle();
$this->entity->updateElasticsearchEntrySynchronously();
match($this->removeFromIndex) {
true => $this->deleteElasticsearchEntry(),
false => $this->createOrUpdateElasticsearchEntry(),
};
}
private function createOrUpdateElasticsearchEntry()
{
Elasticsearch::connection()->index($this->elasticsearchBody);
}
private function deleteElasticsearchEntry()
{
try {
Elasticsearch::connection()->delete($this->elasticsearchBody);
} catch (Missing404Exception $e) {
// If the entity we're trying to delete isn't indexed in Elasticsearch,
// that's fine.
}
}
}

View file

@ -59,7 +59,7 @@ class Assets
foreach ($styles as $style) {
$filename = self::replaceExtensionWith($style, '.less', '.css');
$retVal .= "<link rel='stylesheet' href='/build/$filename?".filemtime(public_path("/build/${filename}"))."' />";
$retVal .= "<link rel='stylesheet' href='/build/styles/$filename?".filemtime(public_path("/build/styles/${filename}"))."' />";
}
return $retVal;
@ -81,7 +81,7 @@ class Assets
$files = [];
$filesFound = [];
foreach ($globs as $glob) {
foreach (glob('../resources/assets/'.$glob, GLOB_BRACE) as $file) {
foreach (glob('../resources/'.$glob) as $file) {
if (isset($filesFound[$file])) {
continue;
}

View file

@ -24,7 +24,7 @@ class External
{
public static function execute($command)
{
$process = new Process($command);
$process = Process::fromShellCommandline($command);
$process->run();
if (! $process->isSuccessful()) {

View file

@ -48,10 +48,15 @@ class Search
public function searchAllContent(string $query)
{
$results = $this->elasticsearch->msearch([
'index' => $this->index,
'index' => [
$this->index.'-album',
$this->index.'-playlist',
$this->index.'-tracks',
$this->index.'-user',
],
'body' => [
//===== Tracks=====//
['type' => 'track'],
['index' => $this->index.'-track'],
[
'query' => [
'multi_match' => [
@ -70,7 +75,7 @@ class Search
],
//===== Albums =====//
['type' => 'album'],
['index' => $this->index.'-album'],
[
'query' => [
'multi_match' => [
@ -87,7 +92,7 @@ class Search
],
//===== Playlists =====//
['type' => 'playlist'],
['index' => $this->index.'-playlist'],
[
'query' => [
'multi_match' => [
@ -104,7 +109,7 @@ class Search
],
//===== Users =====//
['type' => 'user'],
['index' => $this->index.'-user'],
[
'query' => [
'multi_match' => [

View file

@ -24,6 +24,7 @@ use App\Contracts\Commentable;
use App\Contracts\Favouritable;
use App\Contracts\Searchable;
use App\Exceptions\TrackFileNotFoundException;
use App\Http\Controllers\AlbumsController;
use App\Traits\IndexedInElasticsearchTrait;
use App\Traits\SlugTrait;
use App\Traits\TrackCollection;
@ -211,7 +212,7 @@ class Album extends Model implements Searchable, Commentable, Favouritable
$data['description'] = $album->description;
$data['is_downloadable'] = $is_downloadable;
$data['share'] = [
'url' => action('AlbumsController@getShortlink', ['id' => $album->id]),
'url' => action([AlbumsController::class, 'getShortlink'], ['id' => $album->id]),
'tumblrUrl' => 'http://www.tumblr.com/share/link?url='.urlencode($album->url).'&name='.urlencode($album->title).'&description='.urlencode($album->description),
'twitterUrl' => 'https://platform.twitter.com/widgets/tweet_button.html?text='.$album->title.' by '.$album->user->display_name.' on Pony.fm',
];
@ -279,12 +280,12 @@ class Album extends Model implements Searchable, Commentable, Favouritable
public function getUrlAttribute()
{
return action('AlbumsController@getShow', ['id' => $this->id, 'slug' => $this->slug]);
return action([AlbumsController::class, 'getShow'], ['id' => $this->id, 'slug' => $this->slug]);
}
public function getDownloadUrl($format)
{
return action('AlbumsController@getDownload', ['id' => $this->id, 'extension' => Track::$Formats[$format]['extension']]);
return action([AlbumsController::class, 'getDownload'], ['id' => $this->id, 'extension' => Track::$Formats[$format]['extension']]);
}
public function getCoverUrl($type = Image::NORMAL)

View file

@ -20,6 +20,7 @@
namespace App\Models;
use App\Http\Controllers\ImagesController;
use External;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Config;
@ -169,7 +170,7 @@ class Image extends Model
{
$type = self::$ImageTypes[$type];
return action('ImagesController@getImage', ['id' => $this->id, 'type' => $type['name'], 'extension' => $this->extension]);
return action([ImagesController::class, 'getImage'], ['id' => $this->id, 'type' => $type['name'], 'extension' => $this->extension]);
}
public function getFile($type = self::NORMAL)

View file

@ -24,6 +24,7 @@ use App\Contracts\Commentable;
use App\Contracts\Favouritable;
use App\Contracts\Searchable;
use App\Exceptions\TrackFileNotFoundException;
use App\Http\Controllers\PlaylistsController;
use App\Traits\IndexedInElasticsearchTrait;
use App\Traits\SlugTrait;
use App\Traits\TrackCollection;
@ -173,7 +174,7 @@ class Playlist extends Model implements Searchable, Commentable, Favouritable
$data['comments'] = $comments;
$data['formats'] = $formats;
$data['share'] = [
'url' => action('PlaylistsController@getShortlink', ['id' => $playlist->id]),
'url' => action([PlaylistsController::class, 'getShortlink'], ['id' => $playlist->id]),
'tumblrUrl' => 'http://www.tumblr.com/share/link?url='.urlencode($playlist->url).'&name='.urlencode($playlist->title).'&description='.urlencode($playlist->description),
'twitterUrl' => 'https://platform.twitter.com/widgets/tweet_button.html?text='.$playlist->title.' by '.$playlist->user->display_name.' on Pony.fm',
];
@ -308,12 +309,12 @@ class Playlist extends Model implements Searchable, Commentable, Favouritable
public function getUrlAttribute()
{
return action('PlaylistsController@getPlaylist', ['id' => $this->id, 'slug' => $this->slug]);
return action([PlaylistsController::class, 'getPlaylist'], ['id' => $this->id, 'slug' => $this->slug]);
}
public function getDownloadUrl($format)
{
return action('PlaylistsController@getDownload', ['id' => $this->id, 'format' => Track::$Formats[$format]['extension']]);
return action([PlaylistsController::class, 'getDownload'], ['id' => $this->id, 'format' => Track::$Formats[$format]['extension']]);
}
public function getCoverUrl($type = Image::NORMAL)

View file

@ -24,6 +24,7 @@ use App\Contracts\Commentable;
use App\Contracts\Favouritable;
use App\Contracts\Searchable;
use App\Exceptions\TrackFileNotFoundException;
use App\Http\Controllers\TracksController;
use App\Models\ResourceLogItem;
use App\Traits\IndexedInElasticsearchTrait;
use App\Traits\SlugTrait;
@ -226,7 +227,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
'tag_format' => 'AtomicParsley',
'tag_method' => 'updateTagsWithAtomicParsley',
'mime_type' => 'audio/mp4',
'command' => 'ffmpeg 2>&1 -y -i {$source} -map 0:a -map_metadata -1 -codec:a libfaac -ab 256k -f mp4 {$target}',
'command' => 'ffmpeg 2>&1 -y -i {$source} -map 0:a -map_metadata -1 -codec:a aac -ab 256k -f mp4 {$target}',
],
'ALAC' => [
'index' => 4,
@ -523,8 +524,8 @@ class Track extends Model implements Searchable, Commentable, Favouritable
}
$returnValue['share'] = [
'url' => action('TracksController@getShortlink', ['id' => $track->id]),
'html' => '<iframe src="'.action('TracksController@getEmbed', ['id' => $track->id]).'" width="100%" height="150" allowTransparency="true" frameborder="0" seamless allowfullscreen></iframe>',
'url' => action([TracksController::class, 'getShortlink'], ['id' => $track->id]),
'html' => '<iframe src="'.action([TracksController::class, 'getEmbed'], ['id' => $track->id]).'" width="100%" height="150" allowTransparency="true" frameborder="0" seamless allowfullscreen></iframe>',
'bbcode' => '[url='.$track->url.'][img]'.$track->getCoverUrl().'[/img][/url]',
'twitterUrl' => 'https://platform.twitter.com/widgets/tweet_button.html?text='.$track->title.' by '.$track->user->display_name.' on Pony.fm',
];
@ -813,7 +814,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
*/
public function getUrlAttribute()
{
return action('TracksController@getTrack', ['id' => $this->id, 'slug' => $this->slug]);
return action([TracksController::class, 'getTrack'], ['id' => $this->id, 'slug' => $this->slug]);
}
/**
@ -942,7 +943,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
*/
public function getStreamUrl(string $format = 'MP3', string $apiClientId = null)
{
return action('TracksController@getStream',
return action([TracksController::class, 'getStream'],
[
'id' => $this->id,
'extension' => self::$Formats[$format]['extension'],
@ -1052,7 +1053,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
$format = self::$Formats[$format];
return action('TracksController@getDownload', ['id' => $this->id, 'extension' => $format['extension']]);
return action([TracksController::class, 'getDownload'], ['id' => $this->id, 'extension' => $format['extension']]);
}
/**
@ -1160,9 +1161,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
*/
private function updateTagsWithGetId3(string $format)
{
require_once app_path().'/Library/getid3/getid3/getid3.php';
require_once app_path().'/Library/getid3/getid3/write.php';
$tagWriter = new getid3_writetags;
$tagWriter = new \getid3_writetags;
$tagWriter->overwrite_tags = true;
$tagWriter->tag_encoding = 'UTF-8';
@ -1171,7 +1170,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
$tagWriter->tag_data = [
'title' => [$this->title],
'artist' => [$this->user->display_name],
'year' => [''.$this->year],
'year' => [(string) $this->year],
'genre' => [$this->genre != null ? $this->genre->name : ''],
'comment' => ['Downloaded from: https://pony.fm/'],
'copyright' => ['© '.$this->year.' '.$this->user->display_name],
@ -1185,7 +1184,7 @@ class Track extends Model implements Searchable, Commentable, Favouritable
if ($this->album_id !== null) {
$tagWriter->tag_data['album'] = [$this->album->title];
$tagWriter->tag_data['track'] = [$this->track_number];
$tagWriter->tag_data['track'] = [(string) $this->track_number];
}
if ($format == 'MP3' && $this->cover_id != null && is_file($this->cover->getFile())) {
@ -1250,8 +1249,8 @@ class Track extends Model implements Searchable, Commentable, Favouritable
'title' => $this->title,
'artist' => $this->user->display_name,
'published_at' => $this->published_at ? $this->published_at->toIso8601String() : null,
'genre' => $this->genre->name,
'track_type' => $this->trackType->title,
'genre' => $this->genre?->name,
'track_type' => $this->trackType?->title,
'show_songs' => $this->showSongs->pluck('title'),
];
}

View file

@ -20,6 +20,7 @@
namespace App\Models;
use App\Http\Controllers\TracksController;
use Helpers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
@ -144,7 +145,7 @@ class TrackFile extends Model
public function getUrlAttribute()
{
return action('TracksController@getDownload', ['id' => $this->track_id, 'extension' => $this->extension]);
return action([TracksController::class, 'getDownload'], ['id' => $this->track_id, 'extension' => $this->extension]);
}
public function getSizeAttribute()

View file

@ -22,6 +22,7 @@ namespace App\Models;
use App\Contracts\Commentable;
use App\Contracts\Searchable;
use App\Http\Controllers\ArtistsController;
use App\Traits\IndexedInElasticsearchTrait;
use Carbon\Carbon;
use Gravatar;
@ -346,7 +347,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function getUrlAttribute()
{
return action('ArtistsController@getProfile', $this->slug);
return action([ArtistsController::class, 'getProfile'], $this->slug);
}
public function getMessageUrlAttribute()

View file

@ -20,6 +20,7 @@
namespace App\Providers;
use App\Library\Search;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Validator;
@ -48,12 +49,12 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->bind(Poniverse::class, function (Application $app) {
return new Poniverse($app['config']->get('poniverse.client_id'), $app['config']->get('poniverse.secret'));
$this->app->bind(\Poniverse::class, function (Application $app) {
return new \Poniverse($app['config']->get('poniverse.client_id'), $app['config']->get('poniverse.secret'));
});
$this->app->bind(App\Library\Search::class, function (Application $app) {
return new App\Library\Search(
$this->app->bind(Search::class, function (Application $app) {
return new Search(
\Elasticsearch::connection(),
$app['config']->get('ponyfm.elasticsearch_index')
);

View file

@ -51,7 +51,7 @@ trait IndexedInElasticsearchTrait
});
static::deleted(function (Searchable $entity) {
$entity->updateElasticsearchEntry();
$entity->updateElasticsearchEntry(true);
});
}
@ -62,7 +62,7 @@ trait IndexedInElasticsearchTrait
private function getElasticsearchParameters(bool $includeBody = true)
{
$parameters = [
'index' => config('ponyfm.elasticsearch_index'),
'index' => config('ponyfm.elasticsearch_index')."-".$this->elasticsearchType,
'type' => $this->elasticsearchType,
'id' => $this->id,
];
@ -74,41 +74,15 @@ trait IndexedInElasticsearchTrait
return $parameters;
}
private function createOrUpdateElasticsearchEntry()
{
Elasticsearch::connection()->index($this->getElasticsearchParameters());
}
private function deleteElasticsearchEntry()
{
try {
Elasticsearch::connection()->delete($this->getElasticsearchParameters(false));
} catch (Missing404Exception $e) {
// If the entity we're trying to delete isn't indexed in Elasticsearch,
// that's fine.
}
}
/**
* Asynchronously updates the Elasticsearch entry.
* When in doubt, this is the method to use.
*
* @param bool $removeFromIndex
*/
public function updateElasticsearchEntry()
public function updateElasticsearchEntry(bool $removeFromIndex = false)
{
$job = (new UpdateSearchIndexForEntity($this))->onQueue(config('ponyfm.indexing_queue'));
$job = (new UpdateSearchIndexForEntity($this->getElasticsearchParameters(!$removeFromIndex), $removeFromIndex))->onQueue(config('ponyfm.indexing_queue'));
$this->dispatch($job);
}
/**
* Synchronously updates the Elasticsearch entry. This should only be
* called from the UpdateSearchIndexForEntity job.
*/
public function updateElasticsearchEntrySynchronously()
{
if ($this->shouldBeIndexed()) {
$this->createOrUpdateElasticsearchEntry();
} else {
$this->deleteElasticsearchEntry();
}
}
}

View file

@ -10,7 +10,7 @@
"license": "AGPL",
"type": "project",
"require": {
"php": "^7.4|^8.0",
"php": "^8.0",
"laravel/framework": "^8.27",
"codescale/ffmpeg-php": "2.7.0",
"guzzlehttp/guzzle": "^7.0.1",
@ -20,9 +20,9 @@
"cviebrock/laravel-elasticsearch": "^8.0",
"barryvdh/laravel-debugbar": "^3.5",
"predis/predis": "^1.1",
"ksubileau/color-thief-php": "^1.3",
"ksubileau/color-thief-php": "v2.0.x-dev",
"graham-campbell/exceptions": "^14.0",
"minishlink/web-push": "^1.0",
"minishlink/web-push": "^6.0",
"alsofronie/eloquent-uuid": "^1.0",
"poniverse/api": "dev-rewrite",
"fruitcake/laravel-cors": "2.0.1",
@ -31,7 +31,8 @@
"doctrine/annotations": "^1.11",
"doctrine/cache": "^1.8",
"doctrine/instantiator": "^1.4",
"fideloper/proxy": "^4.4"
"fideloper/proxy": "^4.4",
"james-heinrich/getid3": "^1.9"
},
"require-dev": {
"mockery/mockery": "^1.4.2",
@ -46,6 +47,9 @@
"barryvdh/laravel-ide-helper": "^2.9"
},
"autoload": {
"files": [
"vendor/james-heinrich/getid3/getid3/getid3.php"
],
"classmap": [
"database/migrations",
"app/Library"

1850
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,10 @@ class AddDeletedAtColumnToActivities extends Migration
$table->softDeletes()->index();
});
if ('sqlite' !== DB::getDriverName()) {
// this has issues now, but considering it's about 5 years old and already ran in production
// i don't think we need to worry about making this work :)
/*if ('sqlite' !== DB::getDriverName()) {
// Retroactively fix activities that should be marked as deleted.
// Tracks
DB::table('activities')
@ -64,7 +67,7 @@ class AddDeletedAtColumnToActivities extends Migration
->join('comments', 'activities.resource_id', '=', 'comments.id')
->whereNotNull('comments.deleted_at')
->update(['deleted_at' => DB::raw('comments.deleted_at')]);
}
}*/
}
}

View file

@ -0,0 +1,7 @@
FROM php:8.0-fpm-alpine
RUN docker-php-ext-install mysqli pdo pdo_mysql
COPY --from=composer /usr/bin/composer /usr/bin/composer
ENTRYPOINT ["composer"]

20
docker/entrypoint.sh Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env sh
set -e
MODE=$1
case $MODE in
web)
php-fpm -D
nginx -g 'pid /tmp/nginx.pid; daemon off;'
;;
worker)
sudo -Esu www-data php artisan queue:listen --queue=default,notifications,indexing --sleep=5 --tries=3
;;
*)
echo "Unknown mode given"
;;
esac

26
docker/nginx/site.conf Normal file
View file

@ -0,0 +1,26 @@
server {
root /app/public;
index index.php index.html index.htm;
client_max_body_size 600m;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location /app/storage/app/datastore {
internal;
alias /app/storage/app/datastore/;
}
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
# Mitigate https://httpoxy.org/ vulnerabilities
fastcgi_param HTTP_PROXY "";
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
}

3
docker/php/php.ini Normal file
View file

@ -0,0 +1,3 @@
[php]
post_max_size = 600M
upload_max_filesize = 600M

View file

@ -94,9 +94,9 @@ gulp.task("webpack-dev-server", function () {
gulp.task("styles-app", function () {
var includedStyles = [
"resources/assets/styles/base/jquery-ui.css",
"resources/assets/styles/base/colorbox.css",
"resources/assets/styles/app.less"
"resources/styles/base/jquery-ui.css",
"resources/styles/base/colorbox.css",
"resources/styles/app.less"
];
if (!argv.production) {
@ -104,7 +104,7 @@ gulp.task("styles-app", function () {
// we want to watch embed files and re-compile them. However, we want
// to leave this path out in production so that embed files are not bloating
// the css file
includedStyles.push("resources/assets/styles/embed.less");
includedStyles.push("resources/styles/embed.less");
// Remove app.less from the cache so that it gets recompiled
var styleCache = plug.cached.caches.styles;
@ -124,7 +124,7 @@ gulp.task("styles-app", function () {
return argv.production
// Production pipeline
? gulp.src(includedStyles, {base: "resources/assets/styles"})
? gulp.src(includedStyles, {base: "resources/styles"})
.pipe(plug.plumber(plumberOptions))
.pipe(plug.if(/\.less/, plug.less()))
.pipe(plug.autoprefixer({
@ -137,7 +137,7 @@ gulp.task("styles-app", function () {
.pipe(gulp.dest("public/build/styles"))
// Development pipeline
: gulp.src(includedStyles, {base: "resources/assets/styles"})
: gulp.src(includedStyles, {base: "resources/styles"})
.pipe(plug.plumber(plumberOptions))
.pipe(plug.cached("styles"))
.pipe(plug.sourcemaps.init())
@ -153,7 +153,7 @@ gulp.task("styles-embed", function () {
// since development-mode watches and builds include the embed styles
// already
return gulp.src(["resources/assets/styles/embed.less"], {base: "resources/assets/styles"})
return gulp.src(["resources/styles/embed.less"], {base: "resources/styles"})
.pipe(plug.less())
.pipe(plug.autoprefixer({
browsers: ["last 2 versions"],
@ -166,7 +166,7 @@ gulp.task("styles-embed", function () {
});
gulp.task('copy:templates', function () {
gulp.src([
return gulp.src([
'public/templates/**/*.html'
])
.pipe(plug.angularTemplatecache({
@ -312,7 +312,7 @@ gulp.task('build', gulp.parallel('webpack-build',
gulp.task("watch-legacy", gulp.series(gulp.parallel("build"), function () {
gulp.watch("resources/assets/styles/**/*.{css,less}", gulp.parallel("styles-app"));
gulp.watch("resources/styles/**/*.{css,less}", gulp.parallel("styles-app"));
}));
gulp.task("watch", gulp.parallel("webpack-dev-server", "email-default", "watch-legacy"));

View file

@ -43,8 +43,7 @@ require 'script!../base/moment'
require '../base/soundmanager2-nodebug'
require 'script!../base/tumblr'
require 'angular-strap'
# Just ignore this, blame webpack
require '../../../../node_modules/angular-strap/dist/angular-strap.tpl'
require 'angular-strap/dist/angular-strap.tpl.js'
require 'chart.js';
require 'angular-chart.js';

View file

@ -18,8 +18,8 @@ module.exports = {
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
entry: {
app: './resources/assets/scripts/app/app.coffee',
embed: './resources/assets/scripts/embed/embed.coffee'
app: './resources/scripts/app/app.coffee',
embed: './resources/scripts/embed/embed.coffee'
},
output: {
path: __dirname + '/public',

2167
yarn.lock

File diff suppressed because it is too large Load diff