Compare commits
50 Commits
9a7aa89362
...
v3.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 667f0acd83 | |||
| 97b018f2bc | |||
| 96846d3cf9 | |||
| e552dbab07 | |||
| 346193983f | |||
| e25b617327 | |||
| b14a77762d | |||
| a989f3e92b | |||
| b8d1a164f1 | |||
| 37d496bbca | |||
| eb931bbb6a | |||
| bc66edb3ce | |||
| e10736d51e | |||
| 9101bab010 | |||
| 1d56e833a8 | |||
| ac28e86e68 | |||
| 97fa5a6f9f | |||
| bf30235e1d | |||
| c46664492d | |||
| 06e5101199 | |||
| 9630f331a2 | |||
| 173986da5e | |||
| ca34ab4b37 | |||
| a156a44130 | |||
| 87198b46a5 | |||
| 40024b69ae | |||
| aa79de5688 | |||
| f7a27e7f62 | |||
| 7772be7dc5 | |||
| d5c7e9e4f5 | |||
| 89450f0b79 | |||
| 23e551374a | |||
| 3e3e76773c | |||
| a6dc77feff | |||
| 3b45563ce5 | |||
| ae697fbaae | |||
| ebdc50a697 | |||
| 3d3029ceb6 | |||
| a2ecb62a05 | |||
| a4bd889d23 | |||
| 4ba362b4fd | |||
| c55bec5b28 | |||
| efba3dff84 | |||
| 81525acb82 | |||
| f39ce73978 | |||
| ebd803282c | |||
| a37789ff8a | |||
| 2e3b848ff5 | |||
| 26c4b2c959 | |||
| c4398c641e |
4
.env.example
Normal file
4
.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
DB_DATABASE=database_name
|
||||
DB_USERNAME=database_username
|
||||
DB_PASSWORD="database_password_for_laravel_app"
|
||||
DB_ROOT_PASSWORD="database_password_for_superuser"
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -20,6 +20,7 @@ public_html/hot
|
||||
db/data/
|
||||
db/dump/
|
||||
static/dist/
|
||||
static/*
|
||||
|
||||
|
||||
storage/*.key
|
||||
@@ -28,4 +29,10 @@ Homestead.yaml
|
||||
Homestead.json
|
||||
/.vagrant
|
||||
.phpunit.result.cache
|
||||
|
||||
legacypublic
|
||||
migrated_artworks_files.tar.gz
|
||||
migrated_thumbnail_files.tar.gz
|
||||
site/.yarn/releases/yarn-1.22.19.cjs
|
||||
site/public/assets
|
||||
redis/*
|
||||
staticassets
|
||||
|
||||
3
Dockerfile
Normal file
3
Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM shinsenter/laravel:latest
|
||||
|
||||
RUN apt update && apt install -y jpegoptim optipng pngquant gifsicle webp libavif-bin
|
||||
38
README.md
38
README.md
@@ -4,4 +4,40 @@ Modernizing the No Agenda Art Generator.
|
||||
|
||||
Since 2010, the [No Agenda Art Generator](https://noagendaartgenerator.com) has been producing album art for the [No Agenda Podcast](https://noagendashow.net) live via community collaboration by the artists that make up the best podcast art creators in the universe.
|
||||
|
||||
In October 2016, the 2.0 release of the Art Generator began based on Laravel 4.2. It has served the podcast and community well, but this project seeks to make it easier for collaborators to contribute, modify, and maintain the art generator while making the product available to more podcasts and artists.
|
||||
In October 2014, the 2.0 release of the Art Generator began based on Laravel 4.2. It has served the podcast and community well, but this project seeks to make it easier for collaborators to contribute, modify, and maintain the art generator while making the product available to more podcasts and artists.
|
||||
|
||||
### License
|
||||
|
||||
The Podcast Art Generator is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
||||
### Contibuted Artwork License
|
||||
|
||||
Artworks submitted by artists to the Podcast Art Generator are licensed under a Creative Commons License.
|
||||
|
||||
By submitting artwork, you are acknowledging you have the right to publish the work and are, by submitting the work, agreeing to place it under the [Creative Commons Attribution-Share Alike 3.0, United States License](http://creativecommons.org/licenses/by-sa/3.0/us/).
|
||||
|
||||
#### Copyright
|
||||
|
||||
##### Copyright © 2010-2023, Paul Couture, Some Rights Reserved.
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
### Built Using:
|
||||
|
||||
### Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
### Laravel License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
podcastartgenerator-app:
|
||||
image: shinsenter/laravel:latest
|
||||
laravel-app:
|
||||
env_file: .env
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: ${CONTAINER_NAME:-pcag-laravel}
|
||||
volumes:
|
||||
- ./site:/var/www/html
|
||||
- ./static:/static
|
||||
- ${PATH_TO_SITE}:/var/www/html
|
||||
- ${PATH_TO_STATIC}:/static
|
||||
- ./nginx/default.conf:/etc/nginx/sites-available/default
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
environment:
|
||||
TZ: UTC
|
||||
PUID: ${UID:-1000}
|
||||
PGID: ${GID:-1000}
|
||||
REDIS_HOST: redis
|
||||
DB_HOST: db
|
||||
DB_DATABASE: laravel
|
||||
DB_USERNAME: root
|
||||
DB_PASSWORD: mydb_p@ssw0rd
|
||||
# LARAVEL_QUEUE_ENABLED: true
|
||||
# LARAVEL_QUEUE_OPTIONS: --timeout=60 --tries=3 redis
|
||||
# LARAVEL_SCHEDULE_ENABLED: true
|
||||
DB_DATABASE: ${LIVE_DB_DATABASE}
|
||||
DB_USERNAME: ${LIVE_DB_USERNAME}
|
||||
DB_PASSWORD: ${LIVE_DB_PASSWORD}
|
||||
LARAVEL_QUEUE_ENABLED: true
|
||||
LARAVEL_QUEUE_OPTIONS: --timeout=60 --tries=3 redis
|
||||
LARAVEL_SCHEDULE_ENABLED: true
|
||||
PHP_OPEN_BASEDIR: "/var/www/html:/static"
|
||||
ports:
|
||||
- "80:80"
|
||||
links:
|
||||
@@ -26,7 +34,7 @@ services:
|
||||
static:
|
||||
image: nginx:alpine
|
||||
volumes:
|
||||
- ./static:/usr/share/nginx/html:ro
|
||||
- ./static:/usr/share/nginx/html
|
||||
environment:
|
||||
TZ: UTC
|
||||
PUID: ${UID:-1000}
|
||||
@@ -35,10 +43,13 @@ services:
|
||||
- "8181:80"
|
||||
db:
|
||||
image: mariadb:latest
|
||||
env_file: .env
|
||||
environment:
|
||||
TZ: UTC
|
||||
MYSQL_ROOT_PASSWORD: mydb_p@ssw0rd
|
||||
MYSQL_DATABASE: laravel
|
||||
MARIADB_ROOT_PASSWORD: ${LIVE_DB_ROOT_PASSWORD}
|
||||
MARIADB_USER: ${LIVE_DB_USERNAME}
|
||||
MARIADB_DATABASE: ${LIVE_DB_DATABASE}
|
||||
MARIADB_PASSWORD: ${LIVE_DB_PASSWORD}
|
||||
volumes:
|
||||
- "./db/data:/var/lib/mysql"
|
||||
- "./db/dump:/docker-entrypoint-initdb.d"
|
||||
|
||||
42
nginx/default.conf
Normal file
42
nginx/default.conf
Normal file
@@ -0,0 +1,42 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
server_name _;
|
||||
root /var/www/html/public;
|
||||
index index.html index.htm index.php;
|
||||
|
||||
# SSL
|
||||
ssl_certificate /etc/ssl/web/server.crt;
|
||||
ssl_certificate_key /etc/ssl/web/server.key;
|
||||
|
||||
# additional config
|
||||
include extra.d/*.conf;
|
||||
|
||||
# health check
|
||||
location /ping {
|
||||
access_log off;
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_read_timeout 5s;
|
||||
fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
||||
}
|
||||
|
||||
location ^~ /livewire {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
# handle .php
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
include snippets/fastcgi-php.conf;
|
||||
}
|
||||
|
||||
# index.php fallback
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
}
|
||||
|
||||
11
nginx/legacy_mappings.conf
Normal file
11
nginx/legacy_mappings.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
#snippets/legacy_mappings.conf
|
||||
|
||||
map $uri $legacy_mapping {
|
||||
~^/assets/(?<filename>.+)$ $filename;
|
||||
default $uri;
|
||||
}
|
||||
|
||||
map $filename $modified_filename {
|
||||
~/(.*) $1---;
|
||||
default $filename;
|
||||
}
|
||||
145
nginx/nginx.conf
Normal file
145
nginx/nginx.conf
Normal file
@@ -0,0 +1,145 @@
|
||||
# Generated by nginxconfig.io
|
||||
# https://www.serverion.com/nginx-config/#?0.domain=_&0.path=%2Fvar%2Fwww%2Fhtml&0.redirect=false&0.force_https=false&0.cert_type=custom&0.ssl_certificate=%2Fetc%2Fssl%2Fweb%2Fserver.crt&0.ssl_certificate_key=%2Fetc%2Fssl%2Fweb%2Fserver.key&0.wordpress&0.proxy_path=%2Fping&0.proxy_pass=unix:%2Fvar%2Frun%2Fphp%2Fphp-fpm.sock&0.index=index.html&content_security_policy=default-src%20'self'%20http:%20https:%20data:%20blob:%20'unsafe-inline';%20frame-ancestors%20'self';&php_server=%2Fvar%2Frun%2Fphp%2Fphp7.3-fpm.sock&expires_media=max&expires_svg=max&expires_fonts=max&user=www-data%20www-data&client_max_body_size=2048&symlink=false
|
||||
|
||||
pcre_jit on;
|
||||
worker_processes auto;
|
||||
worker_rlimit_nofile 100000;
|
||||
user www-data www-data;
|
||||
|
||||
events {
|
||||
worker_connections 65535;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
# define common MIME types
|
||||
include mime.types;
|
||||
|
||||
# define the default MIME type
|
||||
default_type application/octet-stream;
|
||||
|
||||
# disable emitting nginx version
|
||||
server_tokens off;
|
||||
|
||||
# disable the directory listing output
|
||||
autoindex off;
|
||||
|
||||
# disable automatic generation of the "ETag"
|
||||
etag off;
|
||||
|
||||
# disable warnings about uninitialized variables are logged
|
||||
uninitialized_variable_warn off;
|
||||
|
||||
# ======================================================================== #
|
||||
|
||||
# the bucket size for the maps hash table
|
||||
map_hash_bucket_size 256;
|
||||
map_hash_max_size 4096;
|
||||
|
||||
# the bucket size for the server names hash tables
|
||||
server_names_hash_bucket_size 256;
|
||||
server_names_hash_max_size 4096;
|
||||
|
||||
# the bucket size for variables hash tables
|
||||
variables_hash_max_size 4096;
|
||||
variables_hash_bucket_size 4096;
|
||||
|
||||
# ======================================================================== #
|
||||
|
||||
# cache informations about FDs, frequently accessed files
|
||||
# can boost performance, but you need to test those values
|
||||
open_file_cache max=200000 inactive=30s;
|
||||
open_file_cache_valid 30s;
|
||||
open_file_cache_min_uses 1;
|
||||
open_file_cache_errors off;
|
||||
open_log_file_cache max=10 inactive=30s min_uses=1 valid=5m;
|
||||
|
||||
# ======================================================================== #
|
||||
|
||||
# logging
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log error;
|
||||
|
||||
# to boost I/O on HDD we can disable access logs
|
||||
log_not_found off;
|
||||
log_subrequest off;
|
||||
rewrite_log on;
|
||||
|
||||
# copies data between one FD and other from within the kernel
|
||||
# faster than read() + write()
|
||||
sendfile off;
|
||||
sendfile_max_chunk 1m;
|
||||
|
||||
# send headers in one piece, it is better than sending them one by one
|
||||
tcp_nopush on;
|
||||
|
||||
# don't buffer data sent, good for small data bursts in real time
|
||||
tcp_nodelay on;
|
||||
|
||||
# large files can be read and sent using multi-threading
|
||||
# without blocking a worker process
|
||||
aio threads;
|
||||
directio 1m;
|
||||
|
||||
# how to compare modification time
|
||||
ssi on;
|
||||
if_modified_since off;
|
||||
|
||||
# set default size of the slice
|
||||
slice 1m;
|
||||
|
||||
# ======================================================================== #
|
||||
|
||||
# allow the server to close connection on non responding client,
|
||||
# this will free up memory
|
||||
reset_timedout_connection on;
|
||||
|
||||
# timeout for reading client request header -- default: 60
|
||||
client_header_timeout 10s;
|
||||
|
||||
# request timed out -- default: 60
|
||||
client_body_timeout 75s;
|
||||
|
||||
# if the request body size is more than the buffer size, then the entire (or partial)
|
||||
# request body is written into a temporary file
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
# if client stop responding, free up memory -- default: 60
|
||||
send_timeout 30s;
|
||||
|
||||
# server will close connection after this time -- default: 75
|
||||
keepalive_timeout 30s;
|
||||
|
||||
# number of requests client can make over keep-alive
|
||||
keepalive_requests 100000;
|
||||
|
||||
# maximum number and size of buffers
|
||||
# for large headers to read from client request -- default: 4 8k;
|
||||
large_client_header_buffers 4 16k;
|
||||
|
||||
# ======================================================================== #
|
||||
|
||||
# SSL
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
# Diffie-Hellman parameter for DHE ciphersuites
|
||||
ssl_dhparam /etc/ssl/dhparam.pem;
|
||||
|
||||
# Mozilla Intermediate configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
|
||||
# OCSP Stapling
|
||||
# ssl_stapling on;
|
||||
# ssl_stapling_verify on;
|
||||
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
|
||||
resolver_timeout 2s;
|
||||
|
||||
# ======================================================================== #
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
1977
nginx/phpfpm8_2.ini
Normal file
1977
nginx/phpfpm8_2.ini
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
APP_NAME="No Agenda Art Generator"
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:H840ho2ltTKV3IC1FQ333AaU8iW2zsn6ma67qhZWerk=
|
||||
APP_KEY=base64:laravel_key_generated_by_app
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://127.0.0.1
|
||||
|
||||
@@ -9,11 +9,11 @@ LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_HOST=db
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=mydb_p@ssw0rd
|
||||
DB_DATABASE=database_database_from_docker_env
|
||||
DB_USERNAME=database_username_from_docker_env
|
||||
DB_PASSWORD="docker_database_password_from_docker_env"
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
|
||||
51
site/app/Console/Commands/AddMissingArtworkIdsToEpisodes.php
Normal file
51
site/app/Console/Commands/AddMissingArtworkIdsToEpisodes.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AddMissingArtworkIdsToEpisodes extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:artwork-to-episodes';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$oldEpisodes = DB::connection('legacy')->select('SELECT * FROM episodes WHERE artwork_id IS NOT NULL AND published = 1');
|
||||
foreach($oldEpisodes as $oldEpisode) {
|
||||
$this->info('Checking old episode ' . $oldEpisode->show_date);
|
||||
$episode = Episode::where('legacy_id', $oldEpisode->id)->first();
|
||||
$artwork = Artwork::where('legacy_id', $oldEpisode->artwork_id)->first();
|
||||
if ($episode && $artwork) {
|
||||
$this->line('Have artwork ' . $artwork->title . ' for episode ' . $episode->title);
|
||||
$episode->artwork_id = $artwork->id;
|
||||
$episode->timestamps = false;
|
||||
if ($episode->isDirty()) {
|
||||
$this->info('I need to update this.');
|
||||
//$episode->save();
|
||||
} else {
|
||||
$this->line('No Change Needed.');
|
||||
}
|
||||
} else {
|
||||
$this->error('I am lost.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
346
site/app/Console/Commands/AddMissingV3ArtworksCommand.php
Normal file
346
site/app/Console/Commands/AddMissingV3ArtworksCommand.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Podcast;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Carbon\Carbon;
|
||||
use ImageOptimizer;
|
||||
|
||||
class AddMissingV3ArtworksCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:add-missing-v3-artworks-command';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$missing_artworks = [
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/7e5e1ca05eb622e2.png',
|
||||
'title' => 'Show Art 1613',
|
||||
'artist' => 'dirty-jersey-whore',
|
||||
'artist_id' => 1318,
|
||||
'episode' => 1613,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/6ac5a1220e954d10.jpg',
|
||||
'title' => 'No Stinkin\' Title Provided',
|
||||
'artist' => 'dirty-jersey-whore',
|
||||
'artist_id' => 1318,
|
||||
'episode' => 1613,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/eb9c5a07a25db93b.jpg',
|
||||
'title' => 'McMystery',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/16dce3f9249b42e8.png',
|
||||
'title' => 'Blinking Red Lights!',
|
||||
'artist' => 'mountainjay',
|
||||
'artist_id' => 891,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/8a6153d61715434b.jpg',
|
||||
'title' => 'Day One Dictator!',
|
||||
'artist' => 'matthewdropco1972',
|
||||
'artist_id' => 1173,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/bf6322f359b0727f.png',
|
||||
'title' => 'Vivek\'s Notebook',
|
||||
'artist' => 'sir-paul-couture',
|
||||
'artist_id' => 4,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/81e71a8e2b24874b.jpg',
|
||||
'title' => 'Plastic Shoes',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/51f16e95c0e86cc9.jpg',
|
||||
'title' => 'My Transhausen Blewupen',
|
||||
'artist' => 'francisco-scaramanga',
|
||||
'artist_id' => 1377,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/b3b6c7988b5fd7f0.jpeg',
|
||||
'title' => 'McPartner',
|
||||
'artist' => 'dame-kenny-ben',
|
||||
'artist_id' => 1121,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/c9c52179e5eedc6f.png',
|
||||
'title' => 'Red Flashing Lights',
|
||||
'artist' => 'jack-evans',
|
||||
'artist_id' => 1224,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/2a29bb2974959b8a.jpg',
|
||||
'title' => 'Threat Level Swift',
|
||||
'artist' => 'darren-oneill',
|
||||
'artist_id' => 756,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/0ed3c698ba336ea9.jpg',
|
||||
'title' => 'Blinking Lights',
|
||||
'artist' => 'matthewdropco1972',
|
||||
'artist_id' => 1173,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/04658d1ccb786d26.png',
|
||||
'title' => 'Deep State University, In The Morning',
|
||||
'artist' => 'mountainjay',
|
||||
'artist_id' => 891,
|
||||
'episode' => 1614,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/61273125525b7861.jpg',
|
||||
'title' => 'Chad GPT',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/479090be93d46fad.jpg',
|
||||
'title' => 'Chet GPT',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/a533697498ad7f75.png',
|
||||
'title' => 'Word Salad, Roundy Variant',
|
||||
'artist' => 'comic-strip-blogger',
|
||||
'artist_id' => 680,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/bcd81059b0440049.png',
|
||||
'title' => 'word salad (that AI allegedly produces)',
|
||||
'artist' => 'comic-strip-blogger',
|
||||
'artist_id' => 680,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/ddfbcc471e4f265f.png',
|
||||
'title' => 'microphone hand (without show number)',
|
||||
'artist' => 'comic-strip-blogger',
|
||||
'artist_id' => 680,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/c11e2b8a88774fca.png',
|
||||
'title' => 'microphone hand',
|
||||
'artist' => 'comic-strip-blogger',
|
||||
'artist_id' => 680,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/f0c08370c93980b8.jpg',
|
||||
'title' => 'Bye Joe',
|
||||
'artist' => 'darren-oneill',
|
||||
'artist_id' => 756,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/9fc5c17fc0ec547e.jpg',
|
||||
'title' => 'Coof Train',
|
||||
'artist' => 'darren-oneill',
|
||||
'artist_id' => 756,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/99e317f0dd6dd10c.jpg',
|
||||
'title' => 'Comb Your Hair!',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/c579a7ef6ab53869.jpg',
|
||||
'title' => 'Milgram',
|
||||
'artist' => 'clip-custodian',
|
||||
'artist_id' => 1431,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/ba791114371437bc.jpeg',
|
||||
'title' => '67% are Killers',
|
||||
'artist' => 'dame-kenny-ben',
|
||||
'artist_id' => 1121,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/05b40baf17249ea5.jpg',
|
||||
'title' => 'Garage Sale Find',
|
||||
'artist' => 'francisco-scaramanga',
|
||||
'artist_id' => 1377,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/fc626cd5542c71d8.jpg',
|
||||
'title' => 'NA Show Art',
|
||||
'artist' => 'monsieur-pierrey',
|
||||
'artist_id' => 58,
|
||||
'episode' => 1615,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/2dc17c67400465ad.jpg',
|
||||
'title' => 'Merry Christmas Ukraine',
|
||||
'artist' => 'clip-custodian',
|
||||
'artist_id' => 1431,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/18724b09fd552fc0.jpg',
|
||||
'title' => 'THE END IS HERE',
|
||||
'artist' => 'francisco-scaramanga',
|
||||
'artist_id' => 1377,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/cefbe40e1afdf45c.jpg',
|
||||
'title' => 'AI Winter is Coming',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/045db414cd60cf2a.jpg',
|
||||
'title' => 'Space Race Force',
|
||||
'artist' => 'Nessworks',
|
||||
'artist_id' => 1159,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/f817a154c260076f.png',
|
||||
'title' => 'No Agenda Verse',
|
||||
'artist' => 'comic-strip-blogger',
|
||||
'artist_id' => 680,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/618ddba291dbe349.png',
|
||||
'title' => 'NA Show Art',
|
||||
'artist' => 'comic-strip-blogger',
|
||||
'artist_id' => 680,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/322eda9c9e44933c.jpg',
|
||||
'title' => 'Chinese Satellite',
|
||||
'artist' => 'darren-oneill',
|
||||
'artist_id' => 756,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/2c9ecc0646cc49dc.jpeg',
|
||||
'title' => 'Naked No Agenda',
|
||||
'artist' => 'KorrectDaRekard',
|
||||
'artist_id' => 1021,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/96ba274872584092.jpg',
|
||||
'title' => 'NA Show Art',
|
||||
'artist' => 'matthewdropco1972',
|
||||
'artist_id' => 1173,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/af8ab3953ba21524.jpg',
|
||||
'title' => 'False Idols',
|
||||
'artist' => 'matt-boisvert',
|
||||
'artist_id' => 1368,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/ec5e76093063d663.jpg',
|
||||
'title' => 'Collect Them All!',
|
||||
'artist' => 'matt-boisvert',
|
||||
'artist_id' => 1368,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/c28ec70cfd544c9b.jpg',
|
||||
'title' => 'Santa\'s New List',
|
||||
'artist' => 'matt-boisvert',
|
||||
'artist_id' => 1368,
|
||||
'episode' => 1616,
|
||||
],
|
||||
[
|
||||
'url' => 'https://noagenda.dev/old/img/6d228246fb744cb7.jpg',
|
||||
'title' => 'Christmas is Coming',
|
||||
'artist' => 'matt-boisvert',
|
||||
'artist_id' => 1368,
|
||||
'episode' => 1616,
|
||||
],
|
||||
];
|
||||
foreach ($missing_artworks as $art) {
|
||||
$artist = Artist::find($art['artist_id']);
|
||||
$episode = Episode::where('episode_number', $art['episode'])->first();
|
||||
$this->line('Artist: ' . $artist->slug);
|
||||
$this->line('Episode: ' . $episode->episode_date->format('Y-m-d'));
|
||||
$artwork = new Artwork;
|
||||
$artwork->title = $art['title'];
|
||||
$artwork->artist_id = $artist->id;
|
||||
$artwork->created_at = $episode->episode_date->format('Y-m-d ' . now()->format('H:i:s'));
|
||||
$artwork->updated_at = $episode->episode_date->format('Y-m-d ' . now()->format('H:i:s'));
|
||||
$artwork->episode_id = $episode->id;
|
||||
$artwork->podcast_id = 1;
|
||||
$artwork->approved_by = 4;
|
||||
$basename = $episode->episode_date->format('Y/m/')
|
||||
. Str::slug($artist->name)
|
||||
. '_'
|
||||
. Str::slug($art['title'])
|
||||
. '_'
|
||||
. Str::random(8)
|
||||
. '.jpg';
|
||||
$artwork->filename = $basename;
|
||||
$artwork->save();
|
||||
$filename = 'artworks/' . $basename;
|
||||
$thumbnailName = 'thumbnails/' . $basename;
|
||||
$this->line($basename);
|
||||
$img = Image::make(file_get_contents($art['url']))->resize(3000, 3000)->encode('jpg', 100);
|
||||
$thumbImg = Image::make(file_get_contents($art['url']))->resize(512, 512)->encode('jpg', 100);
|
||||
$imgLocation = Storage::disk('static')->put($filename, $img);
|
||||
$thumbLocation = Storage::disk('static')->put($thumbnailName, $thumbImg);
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path($filename));
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path($thumbnailName));
|
||||
}
|
||||
}
|
||||
}
|
||||
125
site/app/Console/Commands/GetMissingArtworkMovedCommand.php
Normal file
125
site/app/Console/Commands/GetMissingArtworkMovedCommand.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\User;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Artwork;
|
||||
use ImageOptimizer;
|
||||
|
||||
class GetMissingArtworkMovedCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:wrapup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$missingArtworks = DB::connection('legacy')->select('SELECT * FROM artworks WHERE id > 30835 AND approved_by IS NOT NULL');
|
||||
foreach ($missingArtworks as $missingArtwork) {
|
||||
$localPath = '/legacypublic' . $missingArtwork->path . '/' . $missingArtwork->filename;
|
||||
$user = User::where('legacy_id', $missingArtwork->user_id)->with('artists')->first();
|
||||
$artwork = Artwork::where('legacy_id', $missingArtwork->id)->first();
|
||||
$episode = Episode::where('legacy_id', $missingArtwork->episode_id)->first();
|
||||
$approver = User::where('legacy_id', $missingArtwork->approved_by)->with('artists')->first();
|
||||
if ($artwork) {
|
||||
$this->line('Artwork ID: ' . $artwork->id);
|
||||
}
|
||||
if ($episode) {
|
||||
$this->line('Episode: ' . $episode->episode_number . ' "' . $episode->title . '"');
|
||||
}
|
||||
if (!$artwork) {
|
||||
$newFilename = $this->uniqueName($episode, $user, $missingArtwork);
|
||||
$state = [
|
||||
'title' => $missingArtwork->title,
|
||||
'description' => '',
|
||||
'artist_id' => $user->artists->first()->id,
|
||||
'episode_id' => $episode->id,
|
||||
'podcast_id' => 1,
|
||||
'overlay_id' => null,
|
||||
'filename' => $newFilename . '.jpg',
|
||||
'created_at' => Carbon::parse($missingArtwork->created_at),
|
||||
'updated_at' => Carbon::parse($missingArtwork->updated_at),
|
||||
'legacy_id' => $missingArtwork->id,
|
||||
'approved_by' => $approver->artists->first()->id,
|
||||
];
|
||||
$artwork = Artwork::factory()->state($state)->create();
|
||||
}
|
||||
$this->line('Artist: ' . $user->artists->first()->name);
|
||||
$this->line($localPath);
|
||||
$filename = 'artworks/' . $artwork->filename;
|
||||
$thumbnailName = 'thumbnails/' . $artwork->filename;
|
||||
if (Storage::disk('static')->exists($filename)) {
|
||||
$this->error($filename . ' already exists. ' . Storage::disk('static')->size($filename));
|
||||
}
|
||||
if (Storage::disk('static')->exists($thumbnailName)) {
|
||||
$this->error($thumbnailName . ' already exists. ' . Storage::disk('static')->size($thumbnailName));
|
||||
}
|
||||
$img = Image::make($localPath)
|
||||
->resize(3000, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100);
|
||||
$thumbImg = Image::make($localPath)
|
||||
->resize(512, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100);
|
||||
$imgLocation = Storage::disk('static')->put($filename, $img);
|
||||
$thumbLocation = Storage::disk('static')->put($thumbnailName, $thumbImg);
|
||||
$size_before = Storage::disk('static')->size($filename);
|
||||
$thumb_size_before = Storage::disk('static')->size($thumbnailName);
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path($filename));
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path($thumbnailName));
|
||||
$size_after = Storage::disk('static')->size($filename);
|
||||
$thumb_size_after = Storage::disk('static')->size($thumbnailName);
|
||||
$diff = $size_before - $size_after;
|
||||
$thumbDiff = $thumb_size_before - $thumb_size_after;
|
||||
$this->line('Filesize before: ' . $size_before);
|
||||
$this->line('Filesize after: ' . $size_after);
|
||||
$this->line('Thumb Filesize before: ' . $thumb_size_before);
|
||||
$this->line('Thumb Filesize after: ' . $thumb_size_after);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkExistingFilename($name) {
|
||||
$checkArtwork = Artwork::where('filename', $name . '.jpg')->count();
|
||||
return $checkArtwork;
|
||||
}
|
||||
|
||||
private function uniqueName($episode, $user, $missingArtwork) {
|
||||
$i = 0;
|
||||
$uniqueFilename = $episode->episode_date->format('Y/m/') . Str::slug($user->artists->first()->name . '-' . $missingArtwork->title) . '_' . $missingArtwork->id;
|
||||
$exists = $this->checkExistingFilename($uniqueFilename);
|
||||
if (!$exists) {
|
||||
return $uniqueFilename;
|
||||
}
|
||||
while(!$exists) {
|
||||
$i++;
|
||||
$uniqueFilename = $uniqueFilename . '_v' . $i;
|
||||
$exists = $this->checkExistingFilename($uniqueFilename);
|
||||
}
|
||||
return $uniqueFilename;
|
||||
}
|
||||
}
|
||||
111
site/app/Console/Commands/ImportMissingLegacyArtworkCommand.php
Normal file
111
site/app/Console/Commands/ImportMissingLegacyArtworkCommand.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Carbon\Carbon;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use ImageOptimizer;
|
||||
use App\Models\User;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Episode;
|
||||
|
||||
class ImportMissingLegacyArtworkCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:import-legacy-artwork {legacy_artwork_id}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Used to import a single legacy artwork item that was missed in the migration.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$legacyArtwork = DB::connection('legacy')->table('artworks')->where('id', $this->argument('legacy_artwork_id'))->first();
|
||||
$legacyEpisode = DB::connection('legacy')->table('episodes')->where('id', $legacyArtwork->episode_id)->first();
|
||||
dump($legacyEpisode);
|
||||
dump($legacyArtwork);
|
||||
$user = User::where('legacy_id', $legacyArtwork->user_id)->with('artists')->first();
|
||||
$artist = $user->artists->first();
|
||||
dump($artist);
|
||||
$podcast = Podcast::find(1);
|
||||
$episode = Episode::where('episode_number', $legacyEpisode->episode_number)->first();
|
||||
dump($episode);
|
||||
$date = Carbon::parse($legacyArtwork->created_at);
|
||||
$basename = $date->format('Y')
|
||||
. '/'
|
||||
. $date->format('m')
|
||||
. '/'
|
||||
. Str::slug($artist->name)
|
||||
. '_'
|
||||
. Str::slug($legacyArtwork->title)
|
||||
. '_'
|
||||
. Str::random(8)
|
||||
. '.jpg';
|
||||
$thumbnailName = 'thumbnails/' . $basename;
|
||||
$artworkName = 'artworks/' . $basename;
|
||||
$artworkExists = Artwork::where('artist_id', $artist->id)
|
||||
->where('episode_id', $episode->id)
|
||||
->where('title', $legacyArtwork->title)
|
||||
->count();
|
||||
$thumbnailExists = false;
|
||||
$artExists = false;
|
||||
dump($artworkExists);
|
||||
dump([$thumbnailName, $artworkName]);
|
||||
if (Storage::disk('static')->exists($thumbnailName)) {
|
||||
$this->line('Thumbnail already exists.');
|
||||
$thumbnailExists = true;
|
||||
}
|
||||
if (Storage::disk('static')->exists($artworkName)) {
|
||||
$this->line('Artwork already exists.');
|
||||
$artExists = true;
|
||||
}
|
||||
dump([$thumbnailExists, $artExists]);
|
||||
if (!$artworkExists) {
|
||||
$artwork = Artwork::factory()->state([
|
||||
'title' => $legacyArtwork->title,
|
||||
'artist_id' => $artist->id,
|
||||
'description' => null,
|
||||
'overlay_id' => null,
|
||||
'podcast_id' => $podcast->id,
|
||||
'episode_id' => $episode->id,
|
||||
'approved_by' => 4,
|
||||
'filename' => $basename,
|
||||
'legacy_filename' => $legacyArtwork->path . '/' . $legacyArtwork->filename,
|
||||
'legacy_id' => $legacyArtwork->id,
|
||||
'created_at' => $legacyArtwork->created_at,
|
||||
'updated_at' => $legacyArtwork->created_at,
|
||||
])->create();
|
||||
$img = Image::make('/var/www/html/naartgen/podcastartgenerator/static' . $artwork->legacy_filename)
|
||||
->resize(3000, null, function($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100)
|
||||
->save(Storage::disk('static')->path('/artworks') . '/' . $artwork->filename);
|
||||
$thumbImg = Image::make('/var/www/html/naartgen/podcastartgenerator/static' . $artwork->legacy_filename)
|
||||
->resize(512, null, function($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100)
|
||||
->save(Storage::disk('static')->path('/thumbnails') . '/' . $artwork->filename);
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path('/artworks/' . $artwork->filename));
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path('/thumbnails/' . $artwork->filename));
|
||||
dump($artwork);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
site/app/Console/Commands/LegacyNginxMappingCommand.php
Normal file
46
site/app/Console/Commands/LegacyNginxMappingCommand.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Artwork;
|
||||
|
||||
class LegacyNginxMappingCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:legacy-nginx-mapping';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$oldLocations = [];
|
||||
$newLocations = [];
|
||||
$this->line('# legacy_mappings.conf');
|
||||
$this->line('');
|
||||
$this->line('map $uri $new_location {');
|
||||
$artworks = Artwork::whereNotNull('legacy_filename')->get();
|
||||
foreach ($artworks as $artwork) {
|
||||
if (!in_array($artwork->legacy_filename, $oldLocations) && !in_array($artwork->legacy_filename, $newLocations)) {
|
||||
$oldLocations[] = $artwork->legacy_filename;
|
||||
$newLocations[] = $artwork->legacy_filename;
|
||||
$this->line(' "' . $artwork->legacy_filename . '" "/legacy-asset/?legacy_filename=' . urlencode($artwork->legacy_filename) . '";');
|
||||
}
|
||||
}
|
||||
$this->line(' default $uri;');
|
||||
$this->line('}');
|
||||
$this->line('');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
|
||||
class MapLegacyArtworkToLegacyEpisodeCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:map-legacy';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Maps legacy artwork to the legacy episode it related to.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$artworks = Artwork::whereNotNull('legacy_id')->get();
|
||||
foreach ($artworks as $artwork) {
|
||||
if ($artwork->id > 76200) {
|
||||
$legacyDetailResponse = $this->getArtworkInfoFromApi($artwork->legacy_id);
|
||||
$response = $legacyDetailResponse->object();
|
||||
if ($response->artwork->episode_id) {
|
||||
$episode = Episode::where('legacy_id', $response->artwork->episode_id)->first();
|
||||
if ($episode) {
|
||||
$artwork->episode_id = $episode->id;
|
||||
$this->line('Artwork ID ' . $artwork->id . ' is mapped to Episode ID ' . $episode->id);
|
||||
if ($artwork->isDirty()) {
|
||||
$this->line('This is a new mapping.');
|
||||
$artwork->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getArtworkInfoFromApi($artwork_legacy_id) {
|
||||
$response = Http::timeout(180)
|
||||
->get('https://noagendaartgenerator.com/artworkapi/' . $artwork_legacy_id,
|
||||
[
|
||||
'p' => '7476',
|
||||
]
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
|
||||
class MapSelectedLegacyArtworkToEpisode extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:map-selected';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Maps the correct artwork selected to the legacy episodes';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$episodes = Episode::all();
|
||||
foreach ($episodes as $episode) {
|
||||
$this->line('Checking episode ' . $episode->episode_number);
|
||||
$legacyEpisodeResponse = $this->getEpisodeFromApi($episode->episode_number);
|
||||
$response = $legacyEpisodeResponse->object();
|
||||
if ($response->episode->artwork_id && $response->episode->artwork_id + 0 > 0) {
|
||||
$selectedArtwork = Artwork::where('legacy_id', $response->episode->artwork_id)->first();
|
||||
if ($selectedArtwork) {
|
||||
$episode->artwork_id = $selectedArtwork->id;
|
||||
$this->line('Artwork ID ' . $selectedArtwork->id . ' marked as episode artwork for episode ' . $episode->episode_number);
|
||||
if ($episode->isDirty()) {
|
||||
$this->line('This is a new mapping.');
|
||||
$episode->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getEpisodeFromApi($episode_legacy_id) {
|
||||
$response = Http::timeout(180)
|
||||
->get('https://noagendaartgenerator.com/episodeapi/' . $episode_legacy_id,
|
||||
[
|
||||
'p' => '7476',
|
||||
]
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
38
site/app/Console/Commands/RefreshLeaderBoardCacheCommand.php
Normal file
38
site/app/Console/Commands/RefreshLeaderBoardCacheCommand.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RefreshLeaderBoardCacheCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'naart:leaderboard-cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clears all leaderboard caches.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->line('Clearing Leaderboard Caches...');
|
||||
cache()->forget('leaderboardTwelveMonths');
|
||||
cache()->forget('leaderboardTwelveMonthsLanding');
|
||||
cache()->forget('leaderboardAllTime');
|
||||
cache()->forget('leaderboardRollingSixMonth');
|
||||
cache()->forget('leaderboardNinetyDays');
|
||||
}
|
||||
}
|
||||
27
site/app/Helpers/pcagHelpers.php
Normal file
27
site/app/Helpers/pcagHelpers.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('numberSuffix')) {
|
||||
function numberSuffix($number) {
|
||||
if (!is_int($number) || $number < 1) {
|
||||
return $number;
|
||||
}
|
||||
|
||||
$lastDigit = $number % 10;
|
||||
$secondLastDigit = ($number / 10) % 10;
|
||||
|
||||
if ($secondLastDigit == 1) {
|
||||
return 'th';
|
||||
}
|
||||
|
||||
switch ($lastDigit) {
|
||||
case 1:
|
||||
return 'st';
|
||||
case 2:
|
||||
return 'nd';
|
||||
case 3:
|
||||
return 'rd';
|
||||
default:
|
||||
return 'th';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,13 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ArtistController extends Controller
|
||||
{
|
||||
@@ -14,7 +20,19 @@ class ArtistController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$artists = Artist::whereHas('artworks')
|
||||
->withCount('artworks')
|
||||
->withCount('episodes')
|
||||
->orderBy('artworks_count', 'desc')
|
||||
->paginate(100);
|
||||
$podcasts = Podcast::where('published', true)->get();
|
||||
return view('profile.artists', [
|
||||
'user' => $user,
|
||||
'pageTitle' => 'Artists',
|
||||
'podcasts' => $podcasts,
|
||||
'artists' => $artists,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,9 +62,26 @@ class ArtistController extends Controller
|
||||
* @param \App\Models\Artist $artist
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(Artist $artist)
|
||||
public function show(Request $request, $slug)
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$artist = Artist::where('slug', $slug)
|
||||
->withCount('episodes')
|
||||
->withCount('artworks')
|
||||
->firstOrFail();
|
||||
$artworks = Artwork::where('artist_id', $artist->id)
|
||||
->with('episode')
|
||||
->with('podcast')
|
||||
->whereNotNull('approved_by')
|
||||
->orderBy('artworks.created_at', 'desc')
|
||||
->paginate($perPage = 92, $columns = ['*'], $pageName = 'artworks');
|
||||
$podcasts = Podcast::where('published', true)->with('episodes')->get();
|
||||
return view('profile.artist', [
|
||||
'user' => $user,
|
||||
'artist' => $artist,
|
||||
'artworks' => $artworks,
|
||||
'podcasts' => $podcasts,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +104,30 @@ class ArtistController extends Controller
|
||||
*/
|
||||
public function update(Request $request, Artist $artist)
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$artist = $user->artists->first();
|
||||
$rules = [
|
||||
'location' => ['string', 'max:255'],
|
||||
'alby' => ['email'],
|
||||
'website' => ['url'],
|
||||
'nasocial' => ['starts_with:@'],
|
||||
'name' => ['string', 'unique:artists', 'max:255'],
|
||||
];
|
||||
$validator = Validator::make($request->all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
return back()
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
}
|
||||
$artist->location = $request->location;
|
||||
$artist->alby = $request->alby;
|
||||
$artist->website = $request->website;
|
||||
$artist->nasocial = $request->nasocial;
|
||||
$artist->name = $request->name;
|
||||
if ($artist->isDirty()) {
|
||||
$artist->save();
|
||||
}
|
||||
return redirect('/profile');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,20 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rules\File;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use ImageOptimizer;
|
||||
|
||||
class ArtworkController extends Controller
|
||||
{
|
||||
@@ -14,7 +27,23 @@ class ArtworkController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$artworks = Artwork::whereNotNull('approved_by')
|
||||
->join('episodes', 'artworks.episode_id', '=', 'episodes.id')
|
||||
->select('artworks.*', DB::raw('episodes.episode_number episode_number'))
|
||||
->with('artist')
|
||||
->with('podcast')
|
||||
->with('episode')
|
||||
->orderBy('episode_number', 'desc')
|
||||
->orderBy('artworks.created_at', 'desc')
|
||||
->paginate($perPage = 52, $columns = ['*'], $pageName = 'artworks');
|
||||
$podcasts = $this->publishedPodcasts();
|
||||
return view('explore.artworks', [
|
||||
'user' => $user,
|
||||
'pageTitle' => 'Explore',
|
||||
'artworks' => $artworks,
|
||||
'podcasts' => $podcasts,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +53,51 @@ class ArtworkController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$podcasts = $this->publishedPodcasts();
|
||||
return view('artworks.submit', [
|
||||
'user' => $user,
|
||||
'pageTitle' => 'Submit New Artwork',
|
||||
'podcasts' => $podcasts,
|
||||
]);
|
||||
}
|
||||
|
||||
public function pendingApproval(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
if ($request->user()->cannot('approve', Artwork::class)) {
|
||||
abort(403);
|
||||
}
|
||||
$artworks = Artwork::whereNull('approved_by')
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(50);
|
||||
$podcasts = $this->publishedPodcasts();
|
||||
return view('artworks.approvals', [
|
||||
'user' => $user,
|
||||
'pageTitle' => 'Approve Artworks',
|
||||
'podcasts' => $podcasts,
|
||||
'artworks' => $artworks,
|
||||
]);
|
||||
}
|
||||
|
||||
public function approve(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
if ($request->user()->cannot('approve', Artwork::class)) {
|
||||
abort(403);
|
||||
}
|
||||
$validated = $request->validate([
|
||||
'artwork_id' => 'required|exists:artworks,id'
|
||||
]);
|
||||
$artwork = Artwork::find($request->artwork_id);
|
||||
if ($artwork->artist_id == $user->artists->first()->id) {
|
||||
return redirect('/approve-artwork');
|
||||
}
|
||||
if (is_null($artwork->approved_by)) {
|
||||
$artwork->approved_by = $user->artists->first()->id;
|
||||
$artwork->save();
|
||||
}
|
||||
return redirect('/approve-artworks');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,20 +106,98 @@ class ArtworkController extends Controller
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
//
|
||||
$validator = Validator::make($request->all(), [
|
||||
'title' => ['required', 'max:255',],
|
||||
'podcast' => ['required', 'exists:podcasts,id',],
|
||||
'description' => ['nullable',],
|
||||
'file' => ['required', 'image', Rule::dimensions()->ratio(1)->minWidth(512),],
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return back()
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
}
|
||||
Log::channel('artwork_import')->info('making new artwork model.');
|
||||
$podcast = Podcast::where('id', $request->podcast)->with(['episodes' => function($query) {
|
||||
$query->orderBy('episode_number', 'desc')->limit(1);
|
||||
}])->first();
|
||||
$episode = $podcast->episodes->first();
|
||||
$artist = auth()->user()->artists()->first();
|
||||
$rawFile = $request->file('file');
|
||||
$yearDirectory = now()->format('Y');
|
||||
$monthDirectory = now()->format('m');
|
||||
$indexPath = $yearDirectory . '/' . $monthDirectory . '/index.htm';
|
||||
$imgDirExists = Storage::disk('static')->exists('artworks/' . $indexPath);
|
||||
$thumbDirExists = Storage::disk('static')->exists('thumbnails/' . $indexPath);
|
||||
if (!$imgDirExists) {
|
||||
Storage::disk('static')->put('artworks/' . $indexPath, '');
|
||||
}
|
||||
if (!$thumbDirExists) {
|
||||
Storage::disk('static')->put('thumbnails/' . $indexPath, '');
|
||||
}
|
||||
$filename = $yearDirectory
|
||||
. '/'
|
||||
. $monthDirectory
|
||||
. '/'
|
||||
. Str::slug($artist->name)
|
||||
. '-'
|
||||
. Str::slug($request->title)
|
||||
. '_'
|
||||
. Str::random(8)
|
||||
. '.jpg';
|
||||
$artwork = Artwork::factory()->state([
|
||||
'title' => $request->title,
|
||||
'artist_id' => $artist->id,
|
||||
'description' => $request->description,
|
||||
'overlay_id' => null,
|
||||
'podcast_id' => $podcast->id,
|
||||
'episode_id' => $episode->id,
|
||||
'filename' => $filename,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
])->create();
|
||||
$img = Image::make($rawFile)->resize(3000, null, function($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100)
|
||||
->save(Storage::disk('static')->path('/artworks') . '/' . $artwork->filename);
|
||||
$thumbImg = Image::make($request->file('file'))->resize(512, null, function($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100)
|
||||
->save(Storage::disk('static')->path('/thumbnails') . '/' . $artwork->filename);
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path('/artworks/' . $artwork->filename));
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path('/thumbnails/' . $artwork->filename));
|
||||
return redirect('/artworks/' . $artwork->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param \App\Models\Artwork $artwork
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param the id of the \App\Models\Artwork $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(Artwork $artwork)
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$artwork = Artwork::where('id', $id)
|
||||
->with('podcast')
|
||||
->with('episode')
|
||||
->with('artist')
|
||||
->first();
|
||||
if (is_null($artwork->approved_by) && $user && $user->id != $artwork->id) {
|
||||
return redirect('artworks');
|
||||
}
|
||||
if (is_null($artwork->approved_by) && !$user) {
|
||||
return redirect('artworks');
|
||||
}
|
||||
return view('artworks.artwork', [
|
||||
'artwork' => $artwork,
|
||||
'user' => $user,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,4 +233,40 @@ class ArtworkController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function legacyArtLink(Request $request, $any = null)
|
||||
{
|
||||
phpinfo();
|
||||
dd($request->path());
|
||||
//$artwork = Artwork::where('legacy_filename', '/assets/artwork/')
|
||||
}
|
||||
|
||||
public function downloadArchiveList(Request $request, $type = 'sd')
|
||||
{
|
||||
$artworks = Artwork::whereNotNull('approved_by')
|
||||
->orderBy('created_at', 'desc')
|
||||
->pluck('filename');
|
||||
$output = '';
|
||||
if ($type == 'sd') {
|
||||
foreach($artworks as $artwork) {
|
||||
$output .= '"https://static.noagendaartgenerator.com/thumbnails/' . $artwork . '"' . "\r\n";
|
||||
}
|
||||
} else {
|
||||
foreach($artworks as $artwork) {
|
||||
$output .= '"https://static.noagendaartgenerator.com/artworks/' . $artwork . '"' . "\r\n";
|
||||
}
|
||||
}
|
||||
return response($output, 200)
|
||||
->header('Content-type', 'text/plain')
|
||||
->header('Content-Length', strlen($output))
|
||||
->header('Content-Disposition', 'attachment; filename="naartgen-archivelist-' . $type . '.txt"');
|
||||
}
|
||||
|
||||
private function publishedPodcasts() {
|
||||
$podcasts = Cache::remember('publishedPodcasts', 30, function() {
|
||||
return Podcast::where('published', true)->get();
|
||||
});
|
||||
return $podcasts;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class PasswordResetLinkController extends Controller
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
'email' => ['required', 'email', 'exists:users,email'],
|
||||
]);
|
||||
|
||||
// We will send the password reset link to this user. Once we have attempted
|
||||
|
||||
@@ -4,10 +4,12 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\Artist;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules;
|
||||
@@ -31,17 +33,24 @@ class RegisteredUserController extends Controller
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'name' => ['unique:artists,name', 'required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'name' => trim($request->name),
|
||||
'email' => trim(strtolower($request->email)),
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
$artist = Artist::create([
|
||||
'user_id' => $user->id,
|
||||
'name' => trim($request->name),
|
||||
'slug' => Str::slug(trim($request->name)),
|
||||
'location' => 'No Agenda Nation',
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Artworks;
|
||||
use App\Models\Episode;
|
||||
|
||||
class EpisodeController extends Controller
|
||||
{
|
||||
@@ -44,9 +48,27 @@ class EpisodeController extends Controller
|
||||
* @param \App\Models\Episode $episode
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(Episode $episode)
|
||||
public function show(Request $request, $podcast_slug, $slug)
|
||||
{
|
||||
//
|
||||
$user = auth()->user();
|
||||
$episode = Episode::where('slug', $slug)
|
||||
->with('approvedArtworks')
|
||||
->with('artwork')
|
||||
->with('podcast')
|
||||
->firstOrFail();
|
||||
$podcasts = Podcast::where('published', true)->with('episodes', function ($query) {
|
||||
$query->orderBy('episode_number', 'desc');
|
||||
$query->where('published', true);
|
||||
$query->take(10);
|
||||
})->get();
|
||||
return view('episodes.episode', [
|
||||
'user' => $user,
|
||||
'pageTitle' => '"' . $episode->title . '" ' . $episode->podcast->name . ' Episode ' . number_format($episode->episode_number + 0),
|
||||
'podcast' => $episode->podcast,
|
||||
'episode' => $episode,
|
||||
'artworks' => $episode->approvedArtworks,
|
||||
'podcasts' => $podcasts,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
295
site/app/Http/Controllers/PageController.php
Normal file
295
site/app/Http/Controllers/PageController.php
Normal file
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Episode;
|
||||
|
||||
|
||||
class PageController extends Controller
|
||||
{
|
||||
public function landing(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$headerCounters = $this->getHeaderCounters();
|
||||
$recentEpisodes = $this->mostRecentEpisodes();
|
||||
$recentSubmissions = $this->mostRecentSubmissions();
|
||||
$leaderboard = $this->leaderboardTwelveMonthsLanding();
|
||||
return view('home.page', [
|
||||
'user' => $user,
|
||||
'pageTitle' => 'Home',
|
||||
'headerCounters' => $headerCounters,
|
||||
'recentEpisodes' => $recentEpisodes,
|
||||
'recentSubmissions' => $recentSubmissions,
|
||||
'leaderboard' => $leaderboard,
|
||||
'preferredTheme' => $request->session()->get('preferred_theme') ?? 'dark',
|
||||
]);
|
||||
}
|
||||
|
||||
public function leaderboards(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$current_year = now()->format('Y');
|
||||
$start_year = 2011;
|
||||
$years = [];
|
||||
while($start_year < $current_year) {
|
||||
$years[] = $start_year;
|
||||
$start_year++;
|
||||
}
|
||||
arsort($years);
|
||||
return view('leaderboards.leaderboards', [
|
||||
'user' => $user,
|
||||
'leaderboardAllTime' => $this->leaderboardAllTime(),
|
||||
'leaderboardPastTwelveMonths' => $this->leaderboardTwelveMonths(),
|
||||
'leaderboardRollingSixMonths' => $this->leaderboardRollingSixMonths(),
|
||||
'leaderboardRollingNinetyDays' => $this->leaderboardRollingNinetyDays(),
|
||||
'leaderboardYears' => $years,
|
||||
]);
|
||||
}
|
||||
|
||||
public function support(Request $request)
|
||||
{
|
||||
return view('home.support.page', [
|
||||
'user' => auth()->user(),
|
||||
'pageTitle' => 'History and Support',
|
||||
'headerCounters' => $this->getHeaderCounters(),
|
||||
'recentEpisodes' => $this->mostRecentEpisodes(),
|
||||
'recentSubmissions' => $this->mostRecentSubmissions(),
|
||||
'leaderboard' => $this->leaderboardTwelveMonthsLanding(),
|
||||
'preferredTheme' => $request->session()->get('preferred_theme') ?? 'dark',
|
||||
]);
|
||||
}
|
||||
|
||||
private function mostRecentSubmissions() {
|
||||
$artworks = Cache::remember('latestSubmissions', 30, function() {
|
||||
return Artwork::whereNotNull('approved_by')
|
||||
->with('artist')
|
||||
->with('episode')
|
||||
->with('podcast')
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit(10)
|
||||
->get();
|
||||
});
|
||||
return $artworks;
|
||||
}
|
||||
|
||||
private function mostRecentEpisodes()
|
||||
{
|
||||
$episodes = Cache::remember('latestEpisodes', 30, function() {
|
||||
return Episode::where('published', true)
|
||||
->whereHas('artwork')
|
||||
->with('podcast')
|
||||
->with('artwork')
|
||||
->with('artwork.artist')
|
||||
->orderBy('episode_number', 'desc')
|
||||
->limit(5)
|
||||
->get();
|
||||
});
|
||||
return $episodes;
|
||||
}
|
||||
|
||||
private function getHeaderCounters()
|
||||
{
|
||||
$headerCounters = [];
|
||||
$artworkCountNumber = Cache::remember('artworkCountNumber', 30, function() {
|
||||
return Artwork::whereNotNull('approved_by')->count();
|
||||
});
|
||||
$artistCountNumber = Cache::remember('artistCountNumber', 30, function() {
|
||||
return Artist::all()->count();
|
||||
});
|
||||
$episodeCountNumber = Cache::remember('episodeCountNumber', 30, function() {
|
||||
return Episode::all()->count();
|
||||
});
|
||||
$headerCounters['Artworks'] = $this->shortNumberCount($artworkCountNumber);
|
||||
$headerCounters['Artists'] = $this->shortNumberCount($artistCountNumber);
|
||||
$headerCounters['Episodes'] = $this->shortNumberCount($episodeCountNumber);
|
||||
return $headerCounters;
|
||||
}
|
||||
private function shortNumberCount($number)
|
||||
{
|
||||
$units = ['', 'K', 'M', 'B', 'T'];
|
||||
for ($i = 0; $number >= 1000; $i++) {
|
||||
$number /= 1000;
|
||||
}
|
||||
return [
|
||||
'number' => $this->numberFormatPrecision($number, 1), //number_format(floatval($number), 1),
|
||||
'unit' => $units[$i],
|
||||
];
|
||||
}
|
||||
|
||||
private function numberFormatPrecision($number, $precision = 2, $separator = '.')
|
||||
{
|
||||
$numberParts = explode($separator, $number);
|
||||
$response = $numberParts[0];
|
||||
if (count($numberParts)>1 && $precision > 0) {
|
||||
$response .= $separator;
|
||||
$response .= substr($numberParts[1], 0, $precision);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function leaderboardByYear($year) {
|
||||
$leaderboard = cache()->remember('leaderboardForYear' . $year, 30, function() {
|
||||
$startDate = Carbon::createFromFormat('Y-m-d', $year . '-01-01')->startOfDay()->format('Y-m-d');
|
||||
$endDate = $startDate->copy()->endOfYear()->format('Y-m-d');
|
||||
$leaderboard = DB::table('episodes')
|
||||
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
|
||||
->join('artists', 'artists.id', '=', 'artworks.artist_id')
|
||||
->select([
|
||||
DB::raw('artists.id as artistId'),
|
||||
DB::raw('artists.name as artistName'),
|
||||
DB::raw('count(artworks.id) as artworkCount')
|
||||
])
|
||||
->where('episodes.published', 1)
|
||||
->where('episodes.episode_date', '>=', $startDate)
|
||||
->where('episodes.episode_date', '<=', $endDate)
|
||||
->groupBy('artistId')
|
||||
->orderByDesc('artworkCount')
|
||||
->limit(50)
|
||||
->get();
|
||||
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
|
||||
return $leaderboard;
|
||||
});
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
private function leaderboardTwelveMonths() {
|
||||
$leaderboard = cache()->remember('leaderboardTwelveMonths', 30, function() {
|
||||
$endDate = now()->endOfDay()->subYear()->format('Y-m-d');
|
||||
$leaderboard = DB::table('episodes')
|
||||
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
|
||||
->join('artists', 'artists.id', '=', 'artworks.artist_id')
|
||||
->select([
|
||||
DB::raw('artists.id as artistId'),
|
||||
DB::raw('artists.name as artistName'),
|
||||
DB::raw('count(artworks.id) as artworkCount')
|
||||
])
|
||||
->where('episodes.published', 1)
|
||||
->where('episodes.episode_date', '>=', $endDate)
|
||||
->groupBy('artistId')
|
||||
->orderByDesc('artworkCount')
|
||||
->limit(50)
|
||||
->get();
|
||||
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
|
||||
return $leaderboard;
|
||||
});
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
private function leaderboardTwelveMonthsLanding() {
|
||||
$leaderboard = cache()->remember('leaderboardTwelveMonthsLanding', 30, function() {
|
||||
$endDate = now()->endOfDay()->subYear()->format('Y-m-d');
|
||||
$leaderboard = DB::table('episodes')
|
||||
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
|
||||
->join('artists', 'artists.id', '=', 'artworks.artist_id')
|
||||
->select([
|
||||
DB::raw('artists.id as artistId'),
|
||||
DB::raw('artists.name as artistName'),
|
||||
DB::raw('count(artworks.id) as artworkCount')
|
||||
])
|
||||
->where('episodes.published', 1)
|
||||
->where('episodes.episode_date', '>=', $endDate)
|
||||
->groupBy('artistId')
|
||||
->orderByDesc('artworkCount')
|
||||
->limit(10)
|
||||
->get();
|
||||
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
|
||||
return $leaderboard;
|
||||
});
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
private function leaderboardAllTime() {
|
||||
$leaderboard = cache()->remember('leaderboardAllTime', 30, function() {
|
||||
$leaderboard = DB::table('episodes')
|
||||
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
|
||||
->join('artists', 'artists.id', '=', 'artworks.artist_id')
|
||||
->select([
|
||||
DB::raw('artists.id as artistId'),
|
||||
DB::raw('artists.name as artistName'),
|
||||
DB::raw('count(artworks.id) as artworkCount')
|
||||
])
|
||||
->where('episodes.published', 1)
|
||||
->groupBy('artistId')
|
||||
->orderByDesc('artworkCount')
|
||||
->limit(100)
|
||||
->get();
|
||||
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
|
||||
return $leaderboard;
|
||||
});
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
private function leaderboardRollingSixMonths() {
|
||||
$leaderboard = cache()->remember('leaderboardRollingSixMonth', 30, function() {
|
||||
$endDate = now()->endOfDay()->subMonths(6)->format('Y-m-d');
|
||||
$leaderboard = DB::table('episodes')
|
||||
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
|
||||
->join('artists', 'artists.id', '=', 'artworks.artist_id')
|
||||
->select([
|
||||
DB::raw('artists.id as artistId'),
|
||||
DB::raw('artists.name as artistName'),
|
||||
DB::raw('count(artworks.id) as artworkCount')
|
||||
])
|
||||
->where('episodes.published', 1)
|
||||
->where('episodes.episode_date', '>=', $endDate)
|
||||
->groupBy('artistId')
|
||||
->orderByDesc('artworkCount')
|
||||
->limit(50)
|
||||
->get();
|
||||
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
|
||||
return $leaderboard;
|
||||
});
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
private function leaderboardRollingNinetyDays() {
|
||||
$leaderboard = cache()->remember('leaderboardNinetyDays', 30, function() {
|
||||
$endDate = now()->endOfDay()->subDays(90)->format('Y-m-d');
|
||||
$leaderboard = DB::table('episodes')
|
||||
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
|
||||
->join('artists', 'artists.id', '=', 'artworks.artist_id')
|
||||
->select([
|
||||
DB::raw('artists.id as artistId'),
|
||||
DB::raw('artists.name as artistName'),
|
||||
DB::raw('count(artworks.id) as artworkCount')
|
||||
])
|
||||
->where('episodes.published', 1)
|
||||
->where('episodes.episode_date', '>=', $endDate)
|
||||
->groupBy('artistId')
|
||||
->orderByDesc('artworkCount')
|
||||
->limit(50)
|
||||
->get();
|
||||
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
|
||||
return $leaderboard;
|
||||
});
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
private function addArtistModelToLeaderboard($leaderboard) {
|
||||
$artistIds = [];
|
||||
foreach ($leaderboard as $lb) {
|
||||
$artistIds[] = $lb->artistId;
|
||||
}
|
||||
$artists = Artist::whereIn('id', $artistIds)->get();
|
||||
foreach ($leaderboard as $lb) {
|
||||
$lb->artist = $artists->where('id', $lb->artistId)->first();
|
||||
}
|
||||
$p = 0;
|
||||
foreach ($leaderboard as $lb) {
|
||||
$p++;
|
||||
$lb->position = $p;
|
||||
}
|
||||
return $leaderboard;
|
||||
}
|
||||
|
||||
public function setSessionTheme(Request $request) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,57 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Artworks;
|
||||
use App\Models\Episode;
|
||||
use App\Http\Resources\LatestEpisodeResource;
|
||||
|
||||
|
||||
class PodcastController extends Controller
|
||||
{
|
||||
//
|
||||
public function show(Request $request, $slug)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$podcast = Podcast::where('slug', $slug)
|
||||
->where('published', true)
|
||||
->firstOrFail();
|
||||
$episodes = Episode::where('published', true)
|
||||
->whereNotNull('artwork_id')
|
||||
->with('artwork')
|
||||
->with('approvedArtworks')
|
||||
->where('podcast_id', $podcast->id)
|
||||
->orderBy('episode_number', 'desc')->paginate(100);
|
||||
$podcasts = Podcast::where('published', true)->with('episodes')->get();
|
||||
return view('podcasts.podcast', [
|
||||
'user' => $user,
|
||||
'pageTitle' => $podcast->name,
|
||||
'podcast' => $podcast,
|
||||
'episodes' => $episodes,
|
||||
'podcasts' => $podcasts,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the latest episode's chosen artwork for third party tools.
|
||||
*
|
||||
* @param $slug
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function latest_artwork(Request $request, $slug)
|
||||
{
|
||||
$podcast = Podcast::with('latestArtwork.artist')
|
||||
->where('slug', $slug)
|
||||
->where('published', true)
|
||||
->firstOrFail();
|
||||
$art = $podcast->latestArtwork;
|
||||
|
||||
return new LatestEpisodeResource($podcast);
|
||||
return response()->json([
|
||||
'episode_number' => optional($podcast->latestEpisode)->episode_number,
|
||||
'artwork' => $art,
|
||||
'artist' => optional($art)->artist,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ class ProfileController extends Controller
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => $request->user(),
|
||||
'artist' => $request->user()->artists->first(),
|
||||
'avatar' => $request->user()->artists->first()->avatar,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class Kernel extends HttpKernel
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
@@ -12,7 +12,7 @@ class TrustProxies extends Middleware
|
||||
*
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
protected $proxies = '*';
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
@@ -27,7 +29,7 @@ class LoginRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'string', 'email'],
|
||||
'login' => ['required', 'string'],
|
||||
'password' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
@@ -41,6 +43,8 @@ class LoginRequest extends FormRequest
|
||||
{
|
||||
$this->ensureIsNotRateLimited();
|
||||
|
||||
|
||||
/*
|
||||
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
@@ -48,7 +52,20 @@ class LoginRequest extends FormRequest
|
||||
'email' => trans('auth.failed'),
|
||||
]);
|
||||
}
|
||||
*/
|
||||
|
||||
$user = User::where('email', $this->login)
|
||||
->orWhere('name', $this->login)
|
||||
->first();
|
||||
|
||||
if (!$user || !Hash::check($this->password, $user->password)) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
throw ValidationException::withMessages([
|
||||
'login' => __('auth.failed'),
|
||||
]);
|
||||
}
|
||||
|
||||
Auth::login($user, $this->boolean('remember'));
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
}
|
||||
|
||||
|
||||
50
site/app/Http/Resources/LatestEpisodeResource.php
Normal file
50
site/app/Http/Resources/LatestEpisodeResource.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class LatestEpisodeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
$podcast = $this->resource;
|
||||
$artwork = $podcast->latestArtwork;
|
||||
$episode = $artwork->episode;
|
||||
$artist = $artwork->artist;
|
||||
|
||||
$static = config('app.static_asset_url');
|
||||
$full = $static . '/artworks/' . $artwork->filename;
|
||||
$thumb = $static . '/thumbnails/' . $artwork->filename;
|
||||
$raw = (float) $episode->episode_number;
|
||||
if (fmod($raw, 1) === 0.0) {
|
||||
// no fractional part
|
||||
$episodeNumber = (int) $raw;
|
||||
} else {
|
||||
// keep one decimal place
|
||||
$episodeNumber = round($raw, 1);
|
||||
}
|
||||
return [
|
||||
'artist_name' => $artist->name,
|
||||
'artist_profile' => url('/artist/' . $artist->slug),
|
||||
'artist_avatar' => $artist->avatar(),
|
||||
|
||||
'artwork_full' => $full,
|
||||
'artwork_thumb' => $thumb,
|
||||
|
||||
'episode_title' => $episode->title,
|
||||
'episode_number' => $episodeNumber,
|
||||
'episode_date' => $episode->episode_date->format('Y-m-d'),
|
||||
'episode_mp3' => $episode->mp3,
|
||||
'podcast_title' => $podcast->name,
|
||||
'podcast_archive' => url('/podcasts/' . $podcast->slug),
|
||||
];
|
||||
}
|
||||
}
|
||||
68
site/app/Jobs/ImportLegacyUserJob.php
Normal file
68
site/app/Jobs/ImportLegacyUserJob.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\User;
|
||||
use App\Models\Artist;
|
||||
use Carbon\Carbon;
|
||||
use App\Jobs\StashAndOptimizeLegacyArtworkJob;
|
||||
|
||||
class ImportLegacyUserJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$name = str_replace(' ', '', $this->user->username);
|
||||
$email = strtolower(trim($this->user->email));
|
||||
$email_verified_at = Carbon::parse($this->user->created_at);
|
||||
$this->createUser($name, $email, $email_verified_at);
|
||||
}
|
||||
|
||||
private function createUser($name, $email, $email_verified_at)
|
||||
{
|
||||
$user = User::where('name', $name)->first();
|
||||
if (!$user) {
|
||||
$user = User::factory()->state([
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'email_verified_at' => $email_verified_at,
|
||||
'remember_token' => null,
|
||||
])->create();
|
||||
}
|
||||
$artist = Artist::where('user_id', $user->id)->first();
|
||||
if (!$artist) {
|
||||
$artist = Artist::factory()->state([
|
||||
'user_id' => $user->id,
|
||||
'name' => $this->user->profile->name,
|
||||
'avatar' => null,
|
||||
'header' => null,
|
||||
'location' => $this->user->profile->location ?? 'No Agenda Art Generator',
|
||||
'website' => $this->user->profile->website ?? null,
|
||||
'bio' => null,
|
||||
])->create();
|
||||
}
|
||||
foreach ($this->user->artworks as $artwork) {
|
||||
StashAndOptimizeLegacyArtworkJob::dispatch($artist, $artwork);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
site/app/Jobs/OptimizeArtistHeaderJob.php
Normal file
36
site/app/Jobs/OptimizeArtistHeaderJob.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use ImageOptimizer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class OptimizeArtistHeaderJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::info('Header: Optimizing ' . $this->path);
|
||||
ImageOptimizer::optimize($this->path);
|
||||
}
|
||||
}
|
||||
36
site/app/Jobs/OptimizeAvatarJob.php
Normal file
36
site/app/Jobs/OptimizeAvatarJob.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use ImageOptimizer;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class OptimizeAvatarJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::info('Avatar: Optimizing ' . $this->path);
|
||||
ImageOptimizer::optimize($this->path);
|
||||
}
|
||||
}
|
||||
125
site/app/Jobs/StashAndOptimizeLegacyArtworkJob.php
Normal file
125
site/app/Jobs/StashAndOptimizeLegacyArtworkJob.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\User;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Carbon\Carbon;
|
||||
use ImageOptimizer;
|
||||
|
||||
class StashAndOptimizeLegacyArtworkJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $artist;
|
||||
|
||||
protected $artwork;
|
||||
|
||||
public $tries = 2;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($artist, $artwork)
|
||||
{
|
||||
$this->artist = $artist;
|
||||
$this->artwork = $artwork;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$date = Carbon::parse($this->artwork->created_at);
|
||||
$basename = $date->format('Y')
|
||||
. '/' . $date->format('m')
|
||||
. '/' . Str::slug($this->artist->name)
|
||||
. '-' . Str::slug($this->artwork->title)
|
||||
. '_' . $this->artwork->id . '.jpg';
|
||||
$filename = 'artworks/' . $basename;
|
||||
$thumbnailName = 'thumbnails/' . $basename;
|
||||
if (Storage::disk('static')->exists($filename)) {
|
||||
Log::channel('artwork_import')->error($filename . ' already exists. Filesize: ' . Storage::disk('static')->size($filename));
|
||||
$this->createArtwork($basename);
|
||||
return;
|
||||
}
|
||||
$img = Image::make('/legacypublic' . $this->artwork->path . '/' . $this->artwork->filename)
|
||||
->resize(3000, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100);
|
||||
$thumbImg = Image::make('/legacypublic' . $this->artwork->path . '/' . $this->artwork->filename)
|
||||
->resize(512, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
})
|
||||
->encode('jpg', 100);
|
||||
$imgLocation = Storage::disk('static')->put($filename, $img);
|
||||
$thumbLocation = Storage::disk('static')->put($thumbnailName, $thumbImg);
|
||||
$size_before = Storage::disk('static')->size($filename);
|
||||
$thumb_size_before = Storage::disk('static')->size($thumbnailName);
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path($filename));
|
||||
ImageOptimizer::optimize(Storage::disk('static')->path($thumbnailName));
|
||||
$size_after = Storage::disk('static')->size($filename);
|
||||
$thumb_size_after = Storage::disk('static')->size($thumbnailName);
|
||||
$diff = $size_before - $size_after;
|
||||
$thumbDiff = $thumb_size_before - $thumb_size_after;
|
||||
Log::channel('artwork_import')->info('Filesize Before: ' . $size_before);
|
||||
Log::channel('artwork_import')->info('Filesize After: ' . $size_after);
|
||||
$perc_smaller = ($diff / $size_before) * 100;
|
||||
$thumb_perc_smaller = ($thumbDiff / $thumb_size_before) * 100;
|
||||
Log::channel('artwork_import')->info(number_format($perc_smaller, 2) . '% smaller.');
|
||||
Log::channel('artwork_import')->info(number_format($thumb_perc_smaller, 2) . '% smaller thumbnail - ' . $thumb_size_after);
|
||||
Log::channel('artwork_import')->info('Saved and resized ' . $filename);
|
||||
$this->createArtwork($basename);
|
||||
}
|
||||
|
||||
private function createArtwork($basename) {
|
||||
$artwork = Artwork::where('legacy_id', $this->artwork->id)->first();
|
||||
if (!$this->artwork->episode_id) {
|
||||
$episode = Episode::where('episode_date', '>=', Carbon::parse($this->artwork->created_at)->startOfDay())
|
||||
->orderBy('episode_date', 'asc')
|
||||
->first();
|
||||
} else {
|
||||
$episode = Episode::where('legacy_id', $this->artwork->episode_id)->first();
|
||||
}
|
||||
if (!$artwork) {
|
||||
$artwork = Artwork::where('filename', $basename)->first();
|
||||
if ($artwork) {
|
||||
$artwork->legacy_id = $this->artwork->id;
|
||||
$artwork->episode_id = $episode->id ?? null;
|
||||
$artwork->save();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!$artwork) {
|
||||
Log::channel('artwork_import')->info('making new artwork model for ' . $basename);
|
||||
Artwork::factory()->state([
|
||||
'title' => $this->artwork->title,
|
||||
'artist_id' => $this->artist->id,
|
||||
'description' => '',
|
||||
'podcast_id' => 1,
|
||||
'overlay_id' => $this->artwork->overlay_id,
|
||||
'episode_id' => $episode->id ?? null,
|
||||
'filename' => $basename ?? null,
|
||||
'created_at' => Carbon::parse($this->artwork->created_at),
|
||||
'updated_at' => Carbon::parse($this->artwork->updated_at),
|
||||
'legacy_id' => $this->artwork->id,
|
||||
])->create();
|
||||
} else {
|
||||
Log::channel('artwork_import')->info($artwork->id . ' has a model it exists with ' . $artwork->filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
site/app/Livewire/Artist/Avatar.php
Normal file
51
site/app/Livewire/Artist/Avatar.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Artist;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Spatie\Image\Image;
|
||||
use Intervention\Image\Facades\Image as InterventionImage;
|
||||
use ImageOptimizer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rules\File;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Jobs\OptimizeAvatarJob;
|
||||
|
||||
class Avatar extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $avatar;
|
||||
|
||||
public $x;
|
||||
public $y;
|
||||
public $width;
|
||||
public $height;
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.artist.avatar');
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$disk = Storage::disk('static');
|
||||
$avatar = $this->avatar->store('avatars', 'static');
|
||||
Image::load($disk->path($avatar))
|
||||
->manualCrop($this->width, $this->height, $this->x, $this->y)
|
||||
->save();
|
||||
Image::load($disk->path($avatar))
|
||||
->width(350)
|
||||
->height(350)
|
||||
->save();
|
||||
Log::info('Avatar: Optimizing ' . $disk->path($avatar));
|
||||
OptimizeAvatarJob::dispatchSync($disk->path($avatar));
|
||||
//ImageOptimizer::optimize($disk->path($avatar));
|
||||
auth()->user()->artists()->first()->update(compact('avatar'));
|
||||
$this->avatar = null;
|
||||
return redirect(request()->header('Referer'));
|
||||
}
|
||||
}
|
||||
50
site/app/Livewire/Artist/Header.php
Normal file
50
site/app/Livewire/Artist/Header.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Artist;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Spatie\Image\Image;
|
||||
use Intervention\Image\Facades\Image as InterventionImage;
|
||||
use ImageOptimizer;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rules\File;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Jobs\OptimizeArtistHeaderJob;
|
||||
|
||||
class Header extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $header;
|
||||
|
||||
public $x;
|
||||
public $y;
|
||||
public $width;
|
||||
public $height;
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.artist.header');
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$disk = Storage::disk('static');
|
||||
$header = $this->header->store('artist_headers', 'static');
|
||||
Image::load($disk->path($header))
|
||||
->manualCrop($this->width, $this->height, $this->x, $this->y)
|
||||
->save();
|
||||
Image::load($disk->path($header))
|
||||
->width(270)
|
||||
->height(185)
|
||||
->save();
|
||||
Log::info('Avatar: Optimizing ' . $disk->path($header));
|
||||
OptimizeArtistHeaderJob::dispatchSync($disk->path($header));
|
||||
auth()->user()->artists()->first()->update(compact('header'));
|
||||
$this->header = null;
|
||||
return redirect(request()->header('Referer'));
|
||||
}
|
||||
}
|
||||
25
site/app/Livewire/Counter.php
Normal file
25
site/app/Livewire/Counter.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Counter extends Component
|
||||
{
|
||||
public $count = 1;
|
||||
|
||||
public function increment()
|
||||
{
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
public function decrement()
|
||||
{
|
||||
$this->count--;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.counter');
|
||||
}
|
||||
}
|
||||
24
site/app/Livewire/Themeswitch.php
Normal file
24
site/app/Livewire/Themeswitch.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Themeswitch extends Component
|
||||
{
|
||||
|
||||
public function light()
|
||||
{
|
||||
session()->put('preferred_theme', 'light');
|
||||
}
|
||||
|
||||
public function dark()
|
||||
{
|
||||
session()->put('preferred_theme', 'dark');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.themeswitch');
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,31 @@ class Artist extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'slug',
|
||||
'avatar',
|
||||
'header',
|
||||
'location',
|
||||
'alby',
|
||||
'naasocial',
|
||||
'website',
|
||||
'bio',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongs_to(User::class);
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function artworks()
|
||||
@@ -41,4 +63,20 @@ class Artist extends Model
|
||||
return $this->hasMany(Wallet::class);
|
||||
}
|
||||
|
||||
public function avatar()
|
||||
{
|
||||
if (!$this->avatar) {
|
||||
return config('app.static_asset_url') . '/avatars/default_avatar_male.svg';
|
||||
}
|
||||
return config('app.static_asset_url') . '/' . $this->avatar;
|
||||
}
|
||||
|
||||
public function header()
|
||||
{
|
||||
if (!$this->header) {
|
||||
return config('app.static_asset_url') . '/artist_headers/default_artist_banner.png';
|
||||
}
|
||||
return config('app.static_asset_url') . '/' . $this->header;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ class Artwork extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function podcast()
|
||||
{
|
||||
return $this->belongsTo(Podcast::class);
|
||||
|
||||
@@ -14,6 +14,13 @@ class Episode extends Model
|
||||
|
||||
protected $dates = ['episode_date', 'created_at', 'updated_at', 'deleted_at'];
|
||||
|
||||
protected $casts = [
|
||||
'episode_date' => 'date',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function podcast()
|
||||
{
|
||||
return $this->belongsTo(Podcast::class);
|
||||
@@ -21,7 +28,17 @@ class Episode extends Model
|
||||
|
||||
public function artwork()
|
||||
{
|
||||
return $this->hasOne(Artwork::class);
|
||||
return $this->hasOne(Artwork::class, 'id', 'artwork_id');
|
||||
}
|
||||
|
||||
public function approvedArtworks()
|
||||
{
|
||||
return $this->hasMany(Artwork::class)->whereNotNull('artworks.approved_by');
|
||||
}
|
||||
|
||||
public function artworks()
|
||||
{
|
||||
return $this->hasMany(Artwork::class);
|
||||
}
|
||||
|
||||
public function artist()
|
||||
|
||||
@@ -16,6 +16,12 @@ class Overlay extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function artist()
|
||||
{
|
||||
return $this->belongsTo(Artist::class);
|
||||
|
||||
@@ -16,6 +16,13 @@ class Podcast extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'added_at'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
'added_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function episodes()
|
||||
{
|
||||
return $this->hasMany(Episode::class);
|
||||
@@ -26,4 +33,27 @@ class Podcast extends Model
|
||||
return $this->hasManyThrough(Artist::class, Episode::class);
|
||||
}
|
||||
|
||||
public function latestEpisode()
|
||||
{
|
||||
return $this->hasOne(Episode::class)
|
||||
->where('published', true)
|
||||
->orderBy('episode_number', 'desc');
|
||||
}
|
||||
|
||||
public function latestArtwork()
|
||||
{
|
||||
// this follows the hasOneThrough from Episode → Artwork
|
||||
return $this->hasOneThrough(
|
||||
Artwork::class, // final model
|
||||
Episode::class, // intermediate
|
||||
'podcast_id', // FK on episodes
|
||||
'id', // PK on artworks
|
||||
'id', // PK on podcasts
|
||||
'artwork_id' // FK on episodes → artworks
|
||||
)
|
||||
->where('episodes.published', true)
|
||||
->orderBy('episodes.episode_number', 'desc');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ class User extends Authenticatable
|
||||
|
||||
protected $table = 'users';
|
||||
|
||||
protected $dates = ['created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
@@ -44,6 +42,17 @@ class User extends Authenticatable
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be appended.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $appends = [
|
||||
|
||||
];
|
||||
|
||||
public function artists()
|
||||
@@ -56,9 +65,9 @@ class User extends Authenticatable
|
||||
return $this->hasManyThrough(Artwork::class, Artist::class);
|
||||
}
|
||||
|
||||
public function episodes()
|
||||
public function selectedForEpisodes()
|
||||
{
|
||||
return $this->hasManyThrough(Episode::class, Artwork::class);
|
||||
return $this->artists()->first()->episodes;
|
||||
}
|
||||
|
||||
public function wallets()
|
||||
|
||||
@@ -13,6 +13,11 @@ class Wallet extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function walletType()
|
||||
{
|
||||
return $this->hasOne(WalletType::class);
|
||||
|
||||
@@ -13,6 +13,11 @@ class WalletType extends Model
|
||||
|
||||
protected $dates = ['created_at', 'updated_at'];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function wallets()
|
||||
{
|
||||
return $this->hasMany(Wallet::class);
|
||||
|
||||
34
site/app/Policies/ArtworkPolicy.php
Normal file
34
site/app/Policies/ArtworkPolicy.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Artwork;
|
||||
|
||||
class ArtworkPolicy
|
||||
{
|
||||
/**
|
||||
* Create a new policy instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function viewAny(?User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function approve(User $user): bool
|
||||
{
|
||||
if ($user->id == 4) {
|
||||
return true;
|
||||
}
|
||||
$selectedCount = $user->selectedForEpisodes()->count();
|
||||
if ($selectedCount > 5) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use App\Models\Podcast;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -19,6 +22,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
Paginator::useBootstrapFive();
|
||||
$publishedPodcasts = Podcast::where('published', true)->select(['name', 'slug'])->get();
|
||||
View::share(['navPodcasts' => $publishedPodcasts]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace App\Providers;
|
||||
|
||||
// use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use App\Policies\ArtworkPolicy;
|
||||
use App\Models\Artwork;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -13,7 +15,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
//
|
||||
Artwork::class => ArtworkPolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
58
site/app/Providers/Filament/AdminPanelProvider.php
Normal file
58
site/app/Providers/Filament/AdminPanelProvider.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||
use Filament\Pages;
|
||||
use Filament\Panel;
|
||||
use Filament\PanelProvider;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Widgets;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
|
||||
class AdminPanelProvider extends PanelProvider
|
||||
{
|
||||
public function panel(Panel $panel): Panel
|
||||
{
|
||||
return $panel
|
||||
->default()
|
||||
->id('admin')
|
||||
->path('admin')
|
||||
->login()
|
||||
->colors([
|
||||
'primary' => Color::Amber,
|
||||
])
|
||||
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||
->pages([
|
||||
Pages\Dashboard::class,
|
||||
])
|
||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||
->widgets([
|
||||
Widgets\AccountWidget::class,
|
||||
Widgets\FilamentInfoWidget::class,
|
||||
])
|
||||
->middleware([
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
AuthenticateSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
DisableBladeIconComponents::class,
|
||||
DispatchServingFilamentEvent::class,
|
||||
])
|
||||
->authMiddleware([
|
||||
Authenticate::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/dashboard';
|
||||
public const HOME = '/';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, and other route configuration.
|
||||
@@ -28,6 +28,11 @@ class RouteServiceProvider extends ServiceProvider
|
||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
|
||||
RateLimiter::for('password-reset', function (Request $request) {
|
||||
$key = Str::lower($request->input('email')).'|'.$request->ip();
|
||||
return Limit::perHour(5)->by($key);
|
||||
});
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('api')
|
||||
->prefix('api')
|
||||
|
||||
0
site/artisan
Executable file → Normal file
0
site/artisan
Executable file → Normal file
3
site/build.sh
Executable file
3
site/build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
yarn build
|
||||
mv ./public/build/.vite/manifest.json ./public/build/manifest.json
|
||||
@@ -2,16 +2,26 @@
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": ["laravel", "framework"],
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"framework"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"andreiio/blade-remix-icon": "^2.6",
|
||||
"blade-ui-kit/blade-icons": "^1.5",
|
||||
"filament/filament": "^3.0-stable",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"intervention/image": "^2.7",
|
||||
"laravel/framework": "^10.10",
|
||||
"laravel/sanctum": "^3.2",
|
||||
"laravel/sanctum": "^3.3",
|
||||
"laravel/tinker": "^2.8",
|
||||
"livewire/livewire": "^2.12"
|
||||
"livewire/livewire": "^3.2",
|
||||
"mckenziearts/blade-untitledui-icons": "^1.2",
|
||||
"predis/predis": "^2.2",
|
||||
"spatie/image": "^2.2",
|
||||
"spatie/laravel-image-optimizer": "^1.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
@@ -28,7 +38,10 @@
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"app/Helpers/pcagHelpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
@@ -38,7 +51,8 @@
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
"@php artisan package:discover --ansi",
|
||||
"@php artisan filament:upgrade"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
|
||||
4922
site/composer.lock
generated
4922
site/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -59,6 +59,8 @@ return [
|
||||
|
||||
'asset_url' => env('ASSET_URL'),
|
||||
|
||||
'static_asset_url' => env('STATIC_ASSET_URL'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
@@ -167,6 +169,7 @@ return [
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\Filament\AdminPanelProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
])->toArray(),
|
||||
|
||||
|
||||
183
site/config/blade-icons.php
Normal file
183
site/config/blade-icons.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Icons Sets
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| With this config option you can define a couple of
|
||||
| default icon sets. Provide a key name for your icon
|
||||
| set and a combination from the options below.
|
||||
|
|
||||
*/
|
||||
|
||||
'sets' => [
|
||||
|
||||
// 'default' => [
|
||||
//
|
||||
// /*
|
||||
// |-----------------------------------------------------------------
|
||||
// | Icons Path
|
||||
// |-----------------------------------------------------------------
|
||||
// |
|
||||
// | Provide the relative path from your app root to your SVG icons
|
||||
// | directory. Icons are loaded recursively so there's no need to
|
||||
// | list every sub-directory.
|
||||
// |
|
||||
// | Relative to the disk root when the disk option is set.
|
||||
// |
|
||||
// */
|
||||
//
|
||||
// 'path' => 'resources/svg',
|
||||
//
|
||||
// /*
|
||||
// |-----------------------------------------------------------------
|
||||
// | Filesystem Disk
|
||||
// |-----------------------------------------------------------------
|
||||
// |
|
||||
// | Optionally, provide a specific filesystem disk to read
|
||||
// | icons from. When defining a disk, the "path" option
|
||||
// | starts relatively from the disk root.
|
||||
// |
|
||||
// */
|
||||
//
|
||||
// 'disk' => '',
|
||||
//
|
||||
// /*
|
||||
// |-----------------------------------------------------------------
|
||||
// | Default Prefix
|
||||
// |-----------------------------------------------------------------
|
||||
// |
|
||||
// | This config option allows you to define a default prefix for
|
||||
// | your icons. The dash separator will be applied automatically
|
||||
// | to every icon name. It's required and needs to be unique.
|
||||
// |
|
||||
// */
|
||||
//
|
||||
// 'prefix' => 'icon',
|
||||
//
|
||||
// /*
|
||||
// |-----------------------------------------------------------------
|
||||
// | Fallback Icon
|
||||
// |-----------------------------------------------------------------
|
||||
// |
|
||||
// | This config option allows you to define a fallback
|
||||
// | icon when an icon in this set cannot be found.
|
||||
// |
|
||||
// */
|
||||
//
|
||||
// 'fallback' => '',
|
||||
//
|
||||
// /*
|
||||
// |-----------------------------------------------------------------
|
||||
// | Default Set Classes
|
||||
// |-----------------------------------------------------------------
|
||||
// |
|
||||
// | This config option allows you to define some classes which
|
||||
// | will be applied by default to all icons within this set.
|
||||
// |
|
||||
// */
|
||||
//
|
||||
// 'class' => '',
|
||||
//
|
||||
// /*
|
||||
// |-----------------------------------------------------------------
|
||||
// | Default Set Attributes
|
||||
// |-----------------------------------------------------------------
|
||||
// |
|
||||
// | This config option allows you to define some attributes which
|
||||
// | will be applied by default to all icons within this set.
|
||||
// |
|
||||
// */
|
||||
//
|
||||
// 'attributes' => [
|
||||
// // 'width' => 50,
|
||||
// // 'height' => 50,
|
||||
// ],
|
||||
//
|
||||
// ],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global Default Classes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This config option allows you to define some classes which
|
||||
| will be applied by default to all icons.
|
||||
|
|
||||
*/
|
||||
|
||||
'class' => '',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global Default Attributes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This config option allows you to define some attributes which
|
||||
| will be applied by default to all icons.
|
||||
|
|
||||
*/
|
||||
|
||||
'attributes' => [
|
||||
// 'width' => 50,
|
||||
// 'height' => 50,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global Fallback Icon
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This config option allows you to define a global fallback
|
||||
| icon when an icon in any set cannot be found. It can
|
||||
| reference any icon from any configured set.
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback' => '',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Components
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These config options allow you to define some
|
||||
| settings related to Blade Components.
|
||||
|
|
||||
*/
|
||||
|
||||
'components' => [
|
||||
|
||||
/*
|
||||
|----------------------------------------------------------------------
|
||||
| Disable Components
|
||||
|----------------------------------------------------------------------
|
||||
|
|
||||
| This config option allows you to disable Blade components
|
||||
| completely. It's useful to avoid performance problems
|
||||
| when working with large icon libraries.
|
||||
|
|
||||
*/
|
||||
|
||||
'disabled' => false,
|
||||
|
||||
/*
|
||||
|----------------------------------------------------------------------
|
||||
| Default Icon Component Name
|
||||
|----------------------------------------------------------------------
|
||||
|
|
||||
| This config option allows you to define the name
|
||||
| for the default Icon class component.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => 'icon',
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
@@ -56,6 +56,26 @@ return [
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => false,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'legacy' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('LEGACY_DATABASE_URL'),
|
||||
'host' => env('LEGACY_DB_HOST', '127.0.0.1'),
|
||||
'port' => env('LEGACY_DB_PORT', '3306'),
|
||||
'database' => env('LEGACY_DB_DATABASE', 'forge'),
|
||||
'username' => env('LEGACY_DB_USERNAME', 'forge'),
|
||||
'password' => env('LEGACY_DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
|
||||
66
site/config/image-optimizer.php
Normal file
66
site/config/image-optimizer.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Spatie\ImageOptimizer\Optimizers\Cwebp;
|
||||
use Spatie\ImageOptimizer\Optimizers\Gifsicle;
|
||||
use Spatie\ImageOptimizer\Optimizers\Jpegoptim;
|
||||
use Spatie\ImageOptimizer\Optimizers\Optipng;
|
||||
use Spatie\ImageOptimizer\Optimizers\Pngquant;
|
||||
use Spatie\ImageOptimizer\Optimizers\Svgo;
|
||||
|
||||
return [
|
||||
/*
|
||||
* When calling `optimize` the package will automatically determine which optimizers
|
||||
* should run for the given image.
|
||||
*/
|
||||
'optimizers' => [
|
||||
|
||||
Jpegoptim::class => [
|
||||
'-m72', // set maximum quality to 85%
|
||||
'--strip-all', // this strips out all text information such as comments and EXIF data
|
||||
'--all-progressive', // this will make sure the resulting image is a progressive one
|
||||
],
|
||||
|
||||
Pngquant::class => [
|
||||
'--force', // required parameter for this package
|
||||
],
|
||||
|
||||
Optipng::class => [
|
||||
'-i0', // this will result in a non-interlaced, progressive scanned image
|
||||
'-o2', // this set the optimization level to two (multiple IDAT compression trials)
|
||||
'-quiet', // required parameter for this package
|
||||
],
|
||||
|
||||
Svgo::class => [
|
||||
'--disable=cleanupIDs', // disabling because it is know to cause troubles
|
||||
],
|
||||
|
||||
Gifsicle::class => [
|
||||
'-b', // required parameter for this package
|
||||
'-O3', // this produces the slowest but best results
|
||||
],
|
||||
|
||||
Cwebp::class => [
|
||||
'-m 6', // for the slowest compression method in order to get the best compression.
|
||||
'-pass 10', // for maximizing the amount of analysis pass.
|
||||
'-mt', // multithreading for some speed improvements.
|
||||
'-q 90', // quality factor that brings the least noticeable changes.
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* The directory where your binaries are stored.
|
||||
* Only use this when you binaries are not accessible in the global environment.
|
||||
*/
|
||||
'binary_path' => '',
|
||||
|
||||
/*
|
||||
* The maximum time in seconds each optimizer is allowed to run separately.
|
||||
*/
|
||||
'timeout' => 60,
|
||||
|
||||
/*
|
||||
* If set to `true` all output of the optimizer binaries will be appended to the default log.
|
||||
* You can also set this to a class that implements `Psr\Log\LoggerInterface`.
|
||||
*/
|
||||
'log_optimizer_activity' => false,
|
||||
];
|
||||
159
site/config/livewire.php
Normal file
159
site/config/livewire.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Class Namespace
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| This value sets the root class namespace for Livewire component classes in
|
||||
| your application. This value will change where component auto-discovery
|
||||
| finds components. It's also referenced by the file creation commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'class_namespace' => 'App\\Livewire',
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| View Path
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| This value is used to specify where Livewire component Blade templates are
|
||||
| stored when running file creation commands like `artisan make:livewire`.
|
||||
| It is also used if you choose to omit a component's render() method.
|
||||
|
|
||||
*/
|
||||
|
||||
'view_path' => resource_path('views/livewire'),
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Layout
|
||||
|---------------------------------------------------------------------------
|
||||
| The view that will be used as the layout when rendering a single component
|
||||
| as an entire page via `Route::get('/post/create', CreatePost::class);`.
|
||||
| In this case, the view returned by CreatePost will render into $slot.
|
||||
|
|
||||
*/
|
||||
|
||||
'layout' => 'components.layouts.app',
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Lazy Loading Placeholder
|
||||
|---------------------------------------------------------------------------
|
||||
| Livewire allows you to lazy load components that would otherwise slow down
|
||||
| the initial page load. Every component can have a custom placeholder or
|
||||
| you can define the default placeholder view for all components below.
|
||||
|
|
||||
*/
|
||||
|
||||
'lazy_placeholder' => null,
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Temporary File Uploads
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| Livewire handles file uploads by storing uploads in a temporary directory
|
||||
| before the file is stored permanently. All file uploads are directed to
|
||||
| a global endpoint for temporary storage. You may configure this below:
|
||||
|
|
||||
*/
|
||||
|
||||
'temporary_file_upload' => [
|
||||
'disk' => 'local', // Example: 'local', 's3' | Default: 'default'
|
||||
'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
|
||||
'directory' => 'tmp', // Example: 'tmp' | Default: 'livewire-tmp'
|
||||
'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1'
|
||||
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs...
|
||||
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
|
||||
'mov', 'avi', 'wmv', 'mp3', 'm4a',
|
||||
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
|
||||
],
|
||||
'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
|
||||
],
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Render On Redirect
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines if Livewire will run a component's `render()` method
|
||||
| after a redirect has been triggered using something like `redirect(...)`
|
||||
| Setting this to true will render the view once more before redirecting
|
||||
|
|
||||
*/
|
||||
|
||||
'render_on_redirect' => false,
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Eloquent Model Binding
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| Previous versions of Livewire supported binding directly to eloquent model
|
||||
| properties using wire:model by default. However, this behavior has been
|
||||
| deemed too "magical" and has therefore been put under a feature flag.
|
||||
|
|
||||
*/
|
||||
|
||||
'legacy_model_binding' => false,
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Auto-inject Frontend Assets
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| By default, Livewire automatically injects its JavaScript and CSS into the
|
||||
| <head> and <body> of pages containing Livewire components. By disabling
|
||||
| this behavior, you need to use @livewireStyles and @livewireScripts.
|
||||
|
|
||||
*/
|
||||
|
||||
'inject_assets' => true,
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Navigate (SPA mode)
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| By adding `wire:navigate` to links in your Livewire application, Livewire
|
||||
| will prevent the default link handling and instead request those pages
|
||||
| via AJAX, creating an SPA-like effect. Configure this behavior here.
|
||||
|
|
||||
*/
|
||||
|
||||
'navigate' => [
|
||||
'show_progress_bar' => true,
|
||||
'progress_bar_color' => '#2299dd',
|
||||
],
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| HTML Morph Markers
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| Livewire intelligently "morphs" existing HTML into the newly rendered HTML
|
||||
| after each update. To make this process more reliable, Livewire injects
|
||||
| "markers" into the rendered Blade surrounding @if, @class & @foreach.
|
||||
|
|
||||
*/
|
||||
|
||||
'inject_morph_markers' => true,
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Pagination Theme
|
||||
|---------------------------------------------------------------------------
|
||||
|
|
||||
| When enabling Livewire's pagination feature by using the `WithPagination`
|
||||
| trait, Livewire will use Tailwind templates to render pagination views
|
||||
| on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
|
||||
|
|
||||
*/
|
||||
|
||||
'pagination_theme' => 'bootstrap',
|
||||
];
|
||||
@@ -65,6 +65,13 @@ return [
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'artwork_import' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/artwork_import.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
|
||||
@@ -2,10 +2,20 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Artist;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Str;
|
||||
|
||||
class ArtistFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Artist::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
@@ -13,8 +23,19 @@ class ArtistFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$name = fake()->name();
|
||||
$slug = Str::slug(strtolower(trim($name)));
|
||||
return [
|
||||
//
|
||||
'user_id' => User::factory(),
|
||||
'name' => $name,
|
||||
'slug' => $slug,
|
||||
'avatar' => fake()->imageUrl(512, 512),
|
||||
'header' => fake()->imageUrl(270, 185),
|
||||
'location' => fake()->city() . ', ' . fake()->state(),
|
||||
'alby' => null,
|
||||
'naasocial' => null,
|
||||
'website' => rand(0, 1) ? fake()->url : null,
|
||||
'bio' => fake()->paragraphs(rand(1, 3), true),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,23 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Overlay;
|
||||
|
||||
class ArtworkFactory extends Factory
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the factory's corresponding model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Artwork::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
@@ -13,8 +27,19 @@ class ArtworkFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$created = fake()->dateTimeThisDecade();
|
||||
return [
|
||||
//
|
||||
'title' => fake()->name(),
|
||||
'description' => rand(0, 1) ? fake()->paragraphs(rand(1, 3), true) : null,
|
||||
'artist_id' => Artist::factory(),
|
||||
'podcast_id' => Podcast::factory(),
|
||||
'episode_id' => Episode::factory(),
|
||||
'overlay_id' => rand(0, 1) ? Overlay::factory() : null,
|
||||
'filename' => fake()->imageUrl(3000, 3000),
|
||||
'created_at' => $created,
|
||||
'updated_at' => $created,
|
||||
'legacy_id' => null,
|
||||
'approved_by' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,24 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Overlay;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class EpisodeFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Episode::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
@@ -13,8 +27,20 @@ class EpisodeFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$title = fake()->name();
|
||||
$slug = Str::slug($title);
|
||||
$created = fake()->dateTimeThisDecade();
|
||||
return [
|
||||
//
|
||||
'podcast_id' => Podcast::factory(),
|
||||
'artwork_id' => Artwork::factory(),
|
||||
'published' => fake()->boolean(),
|
||||
'episode_date' => fake()->dateTimeThisDecade(),
|
||||
'slug' => $slug,
|
||||
'title' => $title,
|
||||
'mp3' => fake()->url(),
|
||||
'created_at' => $created,
|
||||
'updated_at' => $created,
|
||||
'legacy_id' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,24 @@
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Overlay;
|
||||
|
||||
|
||||
class OverlayFactory extends Factory
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the factory's corresponding model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Overlay::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
@@ -13,8 +28,14 @@ class OverlayFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$name = fake()->name();
|
||||
$slug = Str::slug($name);
|
||||
return [
|
||||
//
|
||||
'name' => $name,
|
||||
'artist_id' => Artist::factory(),
|
||||
'podcast_id' => Podcast::factory(),
|
||||
'available' => fake()->boolean(),
|
||||
'filename' => fake()->imageUrl(3000, 3000),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,21 @@ namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Overlay;
|
||||
|
||||
class PodcastFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Podcast::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
@@ -14,12 +26,14 @@ class PodcastFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$name = fake()->name();
|
||||
$slug = Str::slug($name);
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'name' => $name,
|
||||
'description' => fake()->paragraphs(rand(1,3), true),
|
||||
'website' => 'https://' . fake()->domainName(),
|
||||
'feed' => fake()->url(),
|
||||
'slug' => fake()->slug(),
|
||||
'feed' => 'podcast/' . $slug,
|
||||
'slug' => $slug,
|
||||
'published' => fake()->boolean(),
|
||||
'added_at' => fake()->dateTimeThisDecade(),
|
||||
];
|
||||
|
||||
@@ -4,9 +4,23 @@ namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Overlay;
|
||||
use App\Models\User;
|
||||
|
||||
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = User::class;
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
@@ -14,12 +28,14 @@ class UserFactory extends Factory
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$password = Hash::make(Str::random(rand(8, 16)));
|
||||
return [
|
||||
'name' => $this->faker->name(),
|
||||
'email' => $this->faker->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'password' => $password,
|
||||
'remember_token' => Str::random(10),
|
||||
'legacy_id' => null,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use App\Models\User;
|
||||
|
||||
class CreateArtistsTable extends Migration
|
||||
{
|
||||
@@ -15,7 +16,10 @@ class CreateArtistsTable extends Migration
|
||||
{
|
||||
Schema::create('artists', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained();
|
||||
$table->foreignIdFor(User::class)
|
||||
->constrained()
|
||||
->cascadeOnUpdate()
|
||||
->cascadeOnDelete();
|
||||
$table->string('name')->unique();
|
||||
$table->string('avatar')->nullable();
|
||||
$table->string('header')->nullable();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->decimal('episode_number', 8, 1)->after('episode_date')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->dropColumn('episode_number');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->bigInteger('legacy_id')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->dropColumn('legacy_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->bigInteger('legacy_id')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->dropColumn('legacy_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->bigInteger('approved_by')->nullable()->after('filename');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->dropColumn('approved_by');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->integer('legacy_id')->nullable()->unsigned();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('legacy_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->string('legacy_filename')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->dropColumn('legacy_filename');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('theme')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('theme');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artists', function (Blueprint $table) {
|
||||
$table->string('slug')->after('name');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artists', function (Blueprint $table) {
|
||||
$table->dropColumn('slug');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->index('podcast_id');
|
||||
$table->index('artist_id');
|
||||
$table->index('episode_id');
|
||||
$table->index('legacy_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artworks', function (Blueprint $table) {
|
||||
$table->dropIndex('podcast_id');
|
||||
$table->dropIndex('artist_id');
|
||||
$table->dropIndex('episode_id');
|
||||
$table->dropIndex('legacy_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artists', function (Blueprint $table) {
|
||||
$table->index('user_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artists', function (Blueprint $table) {
|
||||
$table->dropIndex('user_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('podcasts', function (Blueprint $table) {
|
||||
$table->index('published');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('podcasts', function (Blueprint $table) {
|
||||
$table->dropIndex('published');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->index('podcast_id');
|
||||
$table->index('artwork_id');
|
||||
$table->index('episode_date');
|
||||
$table->index('published');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->dropIndex('podcast_id');
|
||||
$table->dropIndex('artwork_id');
|
||||
$table->dropIndex('episode_date');
|
||||
$table->dropIndex('published');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->unique('name');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropUnique('name');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('artists', function (Blueprint $table) {
|
||||
$table->string('alby')->nullable()->after('location');
|
||||
$table->string('nasocial')->nullable()->after('location');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('artists', function (Blueprint $table) {
|
||||
$table->dropColumn('alby');
|
||||
$table->dropColumn('nasocial');
|
||||
});
|
||||
}
|
||||
};
|
||||
60
site/database/seeders/EpisodeSeeder.php
Normal file
60
site/database/seeders/EpisodeSeeder.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class EpisodeSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$current_page = 1;
|
||||
$response = $this->getResponseFromApi($current_page);
|
||||
$last_page = $response->object()->last_page;
|
||||
$this->command->info('Last Page: ' . $last_page);
|
||||
$podcast = Podcast::find(1);
|
||||
while ($current_page <= $last_page) {
|
||||
$this->command->info('Getting Page ' . $current_page);
|
||||
foreach ($response->object()->data as $episode) {
|
||||
$podcastEpisode = Episode::where('title', $episode->title)->first();
|
||||
if (!$podcastEpisode) {
|
||||
$podcastEpisode = Episode::factory()->state([
|
||||
'podcast_id' => 1,
|
||||
'episode_date' => Carbon::parse($episode->show_date),
|
||||
'published' => (bool)$episode->published,
|
||||
'artwork_id' => null,
|
||||
'slug' => $episode->episode_number . '_' . Str::slug($episode->title),
|
||||
'title' => $episode->title,
|
||||
'mp3' => $episode->link,
|
||||
'created_at' => Carbon::parse($episode->created_at),
|
||||
'updated_at' => Carbon::parse($episode->updated_at),
|
||||
'legacy_id' => $episode->id ?? null
|
||||
])->create();
|
||||
} else {
|
||||
$podcastEpisode->legacy_id = $episode->id ?? null;
|
||||
if ($podcastEpisode->isDirty()) {
|
||||
$podcastEpisode->save();
|
||||
}
|
||||
}
|
||||
$this->command->info('Created ' . $episode->show_date . ' - (' . $episode->episode_number . ') ' . $episode->title);
|
||||
}
|
||||
$current_page++;
|
||||
$response = $this->getResponseFromApi($current_page);
|
||||
}
|
||||
}
|
||||
|
||||
private function getResponseFromApi($current_page) {
|
||||
$response = Http::timeout(180)
|
||||
->get('https://noagendaartgenerator.com/episodesjson?p=7476&page=' . $current_page);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
31
site/database/seeders/FixLegacyEpisodeSeeder.php
Normal file
31
site/database/seeders/FixLegacyEpisodeSeeder.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Artwork;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facade\Log;
|
||||
|
||||
class FixLegacyEpisodeSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$episodes = Episode::all();
|
||||
foreach ($episodes as $episode) {
|
||||
if (is_null($episode->episode_number) || $episode->episode_number == 0) {
|
||||
$ep_num_arr = explode('_', $episode->slug);
|
||||
$episode->episode_number = $ep_num_arr[0];
|
||||
}
|
||||
if ($episode->isDirty()) {
|
||||
$episode->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
site/database/seeders/MapLegacyIdsSeeder.php
Normal file
17
site/database/seeders/MapLegacyIdsSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MapLegacyIdsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,30 @@ class PodcastSeeder extends Seeder
|
||||
$added = '2019-02-22 20:00:00';
|
||||
$this->createPodcast($name, $description, $website, $slug, $feed, $added);
|
||||
|
||||
$name = 'Rare Encounter';
|
||||
$description = 'AbleKirby and coldacid converse on anime they watch, books and manga they read, games they play,
|
||||
and all the tech stuff that they come across.';
|
||||
$website = 'http://rareencounter.net';
|
||||
$slug = 'rare-encounter';
|
||||
$feed = 'https://rareencounter.net/external.php?name=RSS';
|
||||
$added = '2020-07-16 20:00:00';
|
||||
$this->createPodcast($name, $description, $website, $slug, $feed, $added);
|
||||
|
||||
$name = 'Unrelenting';
|
||||
$description = 'The Unrelenting Podcast is hosted by Gene Naftulvev and Darren O’Neill. It covers politics, technology, pop-culture, and more!';
|
||||
$website = 'http://unrelenting.show';
|
||||
$slug = 'unrelenting';
|
||||
$feed = 'https://www.unrelenting.show/feed/podcast/';
|
||||
$added = '2021-10-29 20:00:00';
|
||||
$this->createPodcast($name, $description, $website, $slug, $feed, $added);
|
||||
|
||||
$name = 'The Boostagram Ball';
|
||||
$description = 'The First Podcast with Value4Value Music hosted by Adam Curry';
|
||||
$website = 'https://boostagramball.com';
|
||||
$slug = 'boostagram-ball';
|
||||
$feed = 'https://mp3s.nashownotes.com/bballrss.xml';
|
||||
$added = '2023-07-29 20:00:00';
|
||||
$this->createPodcast($name, $description, $website, $slug, $feed, $added);
|
||||
}
|
||||
|
||||
private function createPodcast($name, $description, $website, $slug, $feed, $added) {
|
||||
|
||||
105
site/database/seeders/SeedFromOldApiSeeder.php
Normal file
105
site/database/seeders/SeedFromOldApiSeeder.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Episode;
|
||||
use App\Models\Podcast;
|
||||
use App\Models\Overlay;
|
||||
use App\Jobs\StashAndOptimizeLegacyArtworkJob;
|
||||
use App\Jobs\ImportLegacyUserJob;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class SeedFromOldApiSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
//$this->populateLegacyArtworks();
|
||||
//die();
|
||||
$current_page = 0;
|
||||
$totalArtworks = 0;
|
||||
$missingArtworks = 0;
|
||||
$missingModel = 0;
|
||||
$response = $this->getResponseFromApi($current_page);
|
||||
$last_page = $response->object()->users->last_page;
|
||||
while ($current_page <= $last_page) {
|
||||
$this->command->info('Getting Page ' . $current_page);
|
||||
foreach ($response->object()->users->data as $user) {
|
||||
$this->command->line('Getting Art for ' . $user->profile->name . ', found ' . count($user->artworks) . ' artworks.');
|
||||
$totalArtworks += count($user->artworks);
|
||||
$legacyUser = Artist::where('name', $user->profile->name)->first();
|
||||
if (!$legacyUser) {
|
||||
ImportLegacyUserJob::dispatch($user);
|
||||
} else {
|
||||
if ($legacyUser->artworks->count() < count($user->artworks)) {
|
||||
$countDiff = count($user->artworks) - $legacyUser->artworks->count();
|
||||
$missingArtworks += $countDiff;
|
||||
$this->command->comment('Artist ID '
|
||||
. $legacyUser->id
|
||||
. ' '
|
||||
. $legacyUser->name
|
||||
. ' has '
|
||||
. $legacyUser->artworks->count()
|
||||
. ' artworks.');
|
||||
$this->command->error('Missing ' . $countDiff . ' artworks.');
|
||||
foreach ($user->artworks as $artwork) {
|
||||
$date = Carbon::parse($artwork->created_at);
|
||||
$basename = $date->format('Y')
|
||||
. '/' . $date->format('m')
|
||||
. '/' . Str::slug($legacyUser->name)
|
||||
. '-' . Str::slug($artwork->title)
|
||||
. '_' . $artwork->id . '.jpg';
|
||||
if (Storage::disk('static')->exists('artworks/' . $basename)) {
|
||||
$artworkModel = Artwork::where('filename', $basename)->first();
|
||||
if (!$artworkModel) {
|
||||
$missingModel++;
|
||||
$this->command->error($basename . ' exists.');
|
||||
StashAndOptimizeLegacyArtworkJob::dispatch($legacyUser, $artwork);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->command->line('Locally stored all of ' . $legacyUser->name . '\'s artworks.');
|
||||
}
|
||||
}
|
||||
}
|
||||
$current_page++;
|
||||
$response = $this->getResponseFromApi($current_page);
|
||||
}
|
||||
$this->command->info('Total Artworks: ' . $totalArtworks);
|
||||
$this->command->info('Total Missing: ' . $missingArtworks);
|
||||
$this->command->info('Total Missing Model: ' . $missingModel);
|
||||
}
|
||||
|
||||
private function getResponseFromApi($current_page) {
|
||||
$response = Http::timeout(180)
|
||||
->get('https://noagendaartgenerator.com/artistapi',
|
||||
[
|
||||
'p' => '7476',
|
||||
'page' => $current_page,
|
||||
]
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function populateLegacyArtworks() {
|
||||
$artworks = Artwork::whereNull('legacy_id')->get();
|
||||
foreach ($artworks as $artwork) {
|
||||
$file = pathinfo($artwork->filename);
|
||||
$filename = explode('_', $file['filename']);
|
||||
$legacy_id = end($filename);
|
||||
$artwork->legacy_id = $legacy_id;
|
||||
$artwork->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
1779
site/package-lock.json
generated
1779
site/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,21 @@
|
||||
"axios": "^1.1.2",
|
||||
"laravel-vite-plugin": "^0.7.5",
|
||||
"postcss": "^8.4.6",
|
||||
"sass": "^1.63.6",
|
||||
"tailwindcss": "^3.1.0",
|
||||
"vite": "^4.0.0"
|
||||
"vite": "^5.0.9",
|
||||
"wolfy87-eventemitter": "4.2.0"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"aos": "^3.0.0-beta.6",
|
||||
"cropperjs": "^1.6.1",
|
||||
"guillotine": "^1.3.1",
|
||||
"isotope-layout": "^3.0.6",
|
||||
"jquery": "^3.7.0",
|
||||
"jquery-nice-select": "^1.1.0",
|
||||
"js.cookie": "^0.0.4",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"waypoints": "^4.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
740
site/public/adm/adminer.css
Normal file
740
site/public/adm/adminer.css
Normal file
@@ -0,0 +1,740 @@
|
||||
/*
|
||||
* Theme by Douglas Damasio [http://github.com/douglasdamasio]
|
||||
* Based on Pepa Linha
|
||||
* Color syntax inspired by Dracula Theme [https://draculatheme.com/]
|
||||
* @version 1.0 (June 2020)
|
||||
*/
|
||||
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600&family=Ubuntu:wght@500;700&display=swap');
|
||||
|
||||
:root {
|
||||
--color-darkBackground: #282a36;
|
||||
--color-darkCurrentLine: #44475a;
|
||||
--color-darkForeground: #f8f8f2;
|
||||
--color-darkComment: #6272a4;
|
||||
--color-darkCyan: #8be9fd;
|
||||
--color-darkGreen: #50fa7b;
|
||||
--color-darkOrange: #ffb86c;
|
||||
--color-darkPink: #ff79c6;
|
||||
--color-darkPurple: #bd93f9;
|
||||
--color-darkRed: #ff5555;
|
||||
--color-darkYellow: #f1fa8c;
|
||||
|
||||
--color-darkTitleSite: #bcc2cd;
|
||||
--color-darkDraculaSite: #383a59;
|
||||
--color-darkDraculaVSCode: #22222c;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
background: var(--color-darkBackground);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
}
|
||||
|
||||
div {
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: 0;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--color-darkPink);
|
||||
border-bottom: 1px solid var(--color-darkComment);
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.rtl h2 {
|
||||
margin: 0;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 22px;
|
||||
margin: 0 0 10px;
|
||||
border-bottom: 1px solid var(--color-darkComment);
|
||||
padding: 0;
|
||||
padding-bottom: 6px;
|
||||
color: var(--color-darkGreen);
|
||||
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
align-items: center;
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
a {
|
||||
color:var(--color-darkPink);
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--color-darkPurple);
|
||||
}
|
||||
|
||||
a:link:hover,
|
||||
a:visited:hover {
|
||||
color: var(--color-darkPink);
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
|
||||
a[href*=charsets] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 0;
|
||||
padding: 6px;
|
||||
color: var(--color-darkOrange);
|
||||
}
|
||||
|
||||
th {
|
||||
background: none;
|
||||
color: var(--color-darkCyan);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
tbody tr:hover td,
|
||||
tbody tr:hover th {
|
||||
background: var(--color-darkCurrentLine);
|
||||
}
|
||||
|
||||
table:not(.checkable) th {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
#edit-fields th {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
thead td,
|
||||
thead th {
|
||||
background: var(--color-darkComment);
|
||||
}
|
||||
|
||||
thead td,
|
||||
thead td a,
|
||||
thead td a:link:hover,
|
||||
thead td a:visited,
|
||||
thead td a:visited:hover,
|
||||
thead th,
|
||||
thead th a,
|
||||
thead th a:link:hover,
|
||||
thead th a:visited,
|
||||
thead th a:visited:hover {
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
table.checkable,
|
||||
p.links + table,
|
||||
pre + table,
|
||||
#edit-fields,
|
||||
p + table,
|
||||
h3 + table,
|
||||
.scrollable table{
|
||||
border: 1px solid var(--color-darkCurrentLine);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
table.checkable tbody tr:hover td,
|
||||
table.checkable tbody tr:hover th {
|
||||
background: var(--color-darkCurrentLine);
|
||||
}
|
||||
|
||||
.js .checkable .checked td,
|
||||
.js .checkable .checked th {
|
||||
background: var(--color-darkDraculaSite);
|
||||
}
|
||||
|
||||
.js .checkable thead .checked td,
|
||||
.js .checkable thead .checked th {
|
||||
background: var(--color-darkPurple);
|
||||
}
|
||||
|
||||
.odds tbody tr:nth-child(2n) {
|
||||
background: var(--color-darkDraculaVSCode);
|
||||
}
|
||||
|
||||
fieldset {
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
padding-top: 5px;
|
||||
margin: 0 0 15px;
|
||||
border: 0;
|
||||
background: var(--color-darkBackground);
|
||||
}
|
||||
|
||||
fieldset select {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
fieldset input[type=button],
|
||||
fieldset input[type=submit],
|
||||
fieldset p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
fieldset div p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: inline-block;
|
||||
padding: 6px 15px;
|
||||
margin: 0 0 0 -15px;
|
||||
background: var(--color-darkDraculaSite);
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
color: var(--color-darkOrange);
|
||||
}
|
||||
|
||||
legend a,
|
||||
legend a:link:hover {
|
||||
color: var(--color-darkOrange);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
code {
|
||||
background: none;
|
||||
}
|
||||
|
||||
p code,
|
||||
pre code,
|
||||
pre[contenteditable=true] {
|
||||
padding: 10px 15px;
|
||||
display: block;
|
||||
font-size: 17px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
p code + a,
|
||||
p code + a:link:hover,
|
||||
p code + a:visited:hover {
|
||||
margin-left: 15px;
|
||||
position: relative;
|
||||
top: -20px;
|
||||
color: var(--color-darkOrange);
|
||||
font-size: 12px;
|
||||
text-decoration: underline;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 0;
|
||||
margin-left: 400px;
|
||||
margin-right: 54px;
|
||||
padding: 0;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
#content > p {
|
||||
margin-bottom: 15px;
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
.rtl #content {
|
||||
margin: 0;
|
||||
margin-left: 54px;
|
||||
margin-right: 400px;
|
||||
padding: 0;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: 347px;
|
||||
border-right: 1px solid var(--color-darkBackground);
|
||||
box-shadow: inset -1px 0 0 #000000b4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 0;
|
||||
background: var(--color-darkDraculaVSCode);
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#menu h1 {
|
||||
line-height: 50px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#menu h1 a {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
#menu h1 .version {
|
||||
color: var(--color-darkPurple);
|
||||
}
|
||||
|
||||
#menu a {
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
#menu p, #tables {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#menu #dbs {
|
||||
background: var(--color-darkDraculaVSCode);
|
||||
padding: 10px 15px 15px;
|
||||
border: 1px solid var(--color-darkForeground);
|
||||
border-bottom: 0;
|
||||
box-sizing: border-box;
|
||||
color: var(--color-darkCyan);
|
||||
}
|
||||
|
||||
#menu #dbs select {
|
||||
outline: 0;
|
||||
border-color: var(--color-darkComment);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#menu p.links {
|
||||
margin: 0 0 15px;
|
||||
border: 1px solid var(--color-darkForeground);
|
||||
border-top: 0;
|
||||
text-align: center;
|
||||
display: table;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#menu p.links a {
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
display: table-cell;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#menu p.links a:hover {
|
||||
color: var(--color-darkPink);
|
||||
}
|
||||
|
||||
#menu p.links a.active {
|
||||
font-weight: normal;
|
||||
background: var(--color-darkCurrentLine);
|
||||
color: var(--color-darkYellow);
|
||||
}
|
||||
|
||||
.tables-filter {
|
||||
margin-top: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#content p.links {
|
||||
margin: -10px 0 15px;
|
||||
}
|
||||
|
||||
#content p.links a {
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
display: table-cell;
|
||||
border: 1px solid var(--color-darkBackground);
|
||||
}
|
||||
|
||||
#content p.links a,
|
||||
#content p.links a:visited,
|
||||
#content p.links a:hover {
|
||||
color: var(--color-darkCyan);
|
||||
}
|
||||
|
||||
#content p.links a.active {
|
||||
font-weight: normal;
|
||||
border: 1px solid var(--color-darkTitleSite);
|
||||
background: var(--color-darkCurrentLine);
|
||||
}
|
||||
|
||||
#tables {
|
||||
max-height: 100%;
|
||||
margin: 15px -15px 32px !important;
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 0;
|
||||
top: 220px;
|
||||
overflow: hidden !important;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
.rtl #tables {
|
||||
overflow: hidden !important;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
#tables a {
|
||||
float: right;
|
||||
padding: 6px 15px;
|
||||
}
|
||||
|
||||
.rtl #tables a {
|
||||
float: none;
|
||||
}
|
||||
|
||||
#tables .structure, #tables .view {
|
||||
float: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rtl #tables a:first-child,
|
||||
.rtl #tables br + a {
|
||||
float: left;
|
||||
display: block;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#tables a:hover,
|
||||
#tables a:hover + a,
|
||||
#tables a.active,
|
||||
#tables a.active + a {
|
||||
background: var(--color-darkBackground);
|
||||
color: var(--color-darkPink);
|
||||
}
|
||||
|
||||
#tables br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.js .column {
|
||||
background: var(--color-darkDraculaVSCode);
|
||||
}
|
||||
|
||||
.js .checked .column {
|
||||
background: var(--color-darkDraculaVSCode);
|
||||
}
|
||||
|
||||
.pages {
|
||||
left: 400px;
|
||||
background: var(--color-darkCyan);
|
||||
color: var(--color-darkBackground);
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
display: inline-block;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.pages a,
|
||||
.pages a:link,
|
||||
.pages a:link:hover,
|
||||
.pages a:visited,
|
||||
.pages a:visited:hover {
|
||||
color: var(--color-darkBackground);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#breadcrumb {
|
||||
margin: 0;
|
||||
left: 400px;
|
||||
background: none;
|
||||
padding: 0;
|
||||
padding-top: 25px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#breadcrumb a {
|
||||
color: var(--color-darkForeground);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#breadcrumb,
|
||||
#breadcrumb a:hover {
|
||||
color: var(--color-darkTitleSite);
|
||||
}
|
||||
|
||||
.rtl #breadcrumb {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-top: 25px;
|
||||
right: 400px;
|
||||
}
|
||||
|
||||
.logout,
|
||||
.rtl .logout {
|
||||
top: 20px;
|
||||
right: 54px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rtl .logout {
|
||||
right: auto;
|
||||
left: 54px;
|
||||
}
|
||||
|
||||
#logout {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
pre.jush,
|
||||
input:not([type]),
|
||||
input[type="color"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
input[type="tel"],
|
||||
input[type="url"],
|
||||
input[type="text"],
|
||||
input[type="search"] {
|
||||
border: 1px solid var(--color-darkCurrentLine);
|
||||
background-color: var(--color-darkBackground);
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
table:not(#table) input:not([type]),
|
||||
table:not(#table) input[type="color"],
|
||||
table:not(#table) input[type="email"],
|
||||
table:not(#table) input[type="number"],
|
||||
table:not(#table) input[type="password"],
|
||||
table:not(#table) input[type="tel"],
|
||||
table:not(#table) input[type="url"],
|
||||
table:not(#table) input[type="text"],
|
||||
table:not(#table) input[type="search"] {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
input[type=submit],
|
||||
input[type=button] {
|
||||
border: 0;
|
||||
padding: 7px 12px;
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
background: var(--color-darkGreen);
|
||||
color: var(--color-darkBackground);
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
transition: background .4s ease;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
input[type=submit][disabled],
|
||||
input[type=button][disabled] {
|
||||
background: var(--color-darkTitleSite) !important;
|
||||
color: var(--color-darkBackground);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input[type=submit]:hover,
|
||||
input[type=button]:hover,
|
||||
input[type=submit]:focus,
|
||||
input[type=button]:focus {
|
||||
background: var(--color-darkGreen);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.logout input[type=submit] {
|
||||
background: var(--color-darkRed);
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
.logout input[type=submit]:hover {
|
||||
background: var(--color-darkRed);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
input.default,
|
||||
input.default {
|
||||
box-shadow: none;
|
||||
background: var(--color-darkGreen);
|
||||
color: var(--color-darkDraculaVSCode);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
select {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 6px;
|
||||
border: 1px solid var(--color-darkCurrentLine);
|
||||
background-color: var(--color-darkBackground);
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
margin: 18px;
|
||||
color: var(--color-darkOrange);
|
||||
}
|
||||
|
||||
.error,
|
||||
.message {
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
background: var(--color-darkCurrentLine);
|
||||
color: var(--color-darkRed);
|
||||
}
|
||||
|
||||
#logins a,
|
||||
#tables a,
|
||||
#tables span {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#form > p {
|
||||
margin-bottom: 15px;
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
|
||||
#schema .table {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
#schema .table a {
|
||||
display: block;
|
||||
margin: -6px;
|
||||
margin-bottom: 6px;
|
||||
padding: 6px;
|
||||
color: var(--color-darkBackground);
|
||||
background: var(--color-darkPurple);
|
||||
}
|
||||
|
||||
#schema .table br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#schema .table span {
|
||||
display: block;
|
||||
margin-bottom: 1px solid var(--color-darkDraculaVSCode);
|
||||
}
|
||||
|
||||
#lang {
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
right: calc(100% + 8px);
|
||||
z-index: 10;
|
||||
margin-right: -340px;
|
||||
line-height: normal;
|
||||
padding: 0;
|
||||
left: auto;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
#lang select {
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
border: 0;
|
||||
background: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
#lang select option {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.rtl #lang {
|
||||
margin-right: 0;
|
||||
left: 100%;
|
||||
margin-left: -261px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.jush {
|
||||
color: var(--color-darkForeground);
|
||||
}
|
||||
|
||||
.jush a {
|
||||
color: var(--color-darkPurple);
|
||||
}
|
||||
|
||||
.jush-sql a,
|
||||
.jush-sql_code a,
|
||||
.jush-sqlite a,
|
||||
.jush-pgsql a,
|
||||
.jush-mssql a,
|
||||
.jush-oracle a,
|
||||
.jush-simpledb a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.jush-bac,
|
||||
.jush-php_bac,
|
||||
.jush-bra,
|
||||
.jush-mssql_bra,
|
||||
.jush-sqlite_quo {
|
||||
color: var(--color-darkYellow);
|
||||
}
|
||||
|
||||
.jush-php_quo,
|
||||
.jush-quo,
|
||||
.jush-quo_one,
|
||||
.jush-php_eot,
|
||||
.jush-apo,
|
||||
.jush-sql_apo,
|
||||
.jush-sqlite_apo,
|
||||
.jush-sql_quo,
|
||||
.jush-sql_eot {
|
||||
color: var(--color-darkOrange);
|
||||
}
|
||||
|
||||
.jush-num,
|
||||
.jush-clr {
|
||||
color: var(--color-darkPurple);
|
||||
}
|
||||
|
||||
@media print {
|
||||
.logout {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#breadcrumb {
|
||||
position: static;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin-right: -20px;
|
||||
border-top: 20px solid var(--color-darkBackground);
|
||||
border-image: var(--color-darkBackground) 100% 0;
|
||||
border-image-source: var(--color-darkBackground);
|
||||
border-image-slice: 100% 0;
|
||||
border-image-width: 1;
|
||||
border-image-outset: 0;
|
||||
border-image-repeat: stretch;
|
||||
}
|
||||
|
||||
.footer > div {
|
||||
background: var(--color-darkBackground);
|
||||
padding: 0 0 .5em;
|
||||
}
|
||||
1598
site/public/adm/index.php
Normal file
1598
site/public/adm/index.php
Normal file
File diff suppressed because one or more lines are too long
1
site/public/css/filament/filament/app.css
Normal file
1
site/public/css/filament/filament/app.css
Normal file
File diff suppressed because one or more lines are too long
49
site/public/css/filament/forms/forms.css
Normal file
49
site/public/css/filament/forms/forms.css
Normal file
File diff suppressed because one or more lines are too long
1
site/public/css/filament/support/support.css
Normal file
1
site/public/css/filament/support/support.css
Normal file
@@ -0,0 +1 @@
|
||||
.fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}
|
||||
1
site/public/js/filament/filament/app.js
Normal file
1
site/public/js/filament/filament/app.js
Normal file
File diff suppressed because one or more lines are too long
13
site/public/js/filament/filament/echo.js
Normal file
13
site/public/js/filament/filament/echo.js
Normal file
File diff suppressed because one or more lines are too long
1
site/public/js/filament/forms/components/color-picker.js
Normal file
1
site/public/js/filament/forms/components/color-picker.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
123
site/public/js/filament/forms/components/file-upload.js
Normal file
123
site/public/js/filament/forms/components/file-upload.js
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user