Added Download file, play for homebrews and ZIP explorer.
This commit is contained in:
@@ -77,9 +77,11 @@ class FileServerController extends Controller {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
if( !EntryHelpers::fileAlreadyDownloaded($file) ) {
|
||||
EntryHelpers::markFileAsDownloaded($file);
|
||||
$file->increaseDownloadCount();
|
||||
if($request->input('count_download', true)) {
|
||||
if (!EntryHelpers::fileAlreadyDownloaded($file)) {
|
||||
EntryHelpers::markFileAsDownloaded($file);
|
||||
$file->increaseDownloadCount();
|
||||
}
|
||||
}
|
||||
|
||||
return redirect( $this->fs->getDownloadFileUrl( $file) );
|
||||
|
||||
@@ -12,13 +12,12 @@ class ToolsController extends Controller
|
||||
|
||||
public function patcher()
|
||||
{
|
||||
|
||||
return view('tools.patcher');
|
||||
}
|
||||
|
||||
public function directPatch( Request $request, int $entryId, EntryFile $file )
|
||||
{
|
||||
if( $file->entry_id != $entryId ) {
|
||||
if( $file->entry_id != $entryId || $file->state === 'private' ) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
@@ -38,22 +37,28 @@ class ToolsController extends Controller
|
||||
|
||||
public function play( Request $request, int $entryId, EntryFile $file )
|
||||
{
|
||||
if( $file->entry_id != $entryId ) {
|
||||
if( $file->entry_id != $entryId || $file->state === 'private' ) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$service = app(FileServersService::class);
|
||||
|
||||
$patches = [
|
||||
'file' => $service->getDownloadFileUrl( $file ),
|
||||
'name' => $file->entry->title,
|
||||
'outputName' => $file->filename
|
||||
];
|
||||
$emuConfig = [
|
||||
'core' => $file->playOnlineSetting?->core,
|
||||
'threads' => $file->playOnlineSetting?->threads,
|
||||
];
|
||||
|
||||
if( $file->entry->type === 'homebrew' ){
|
||||
$filePath = $service->getDownloadFileUrl( $file );
|
||||
|
||||
return view('tools.play-homebrew', compact('filePath', 'emuConfig'));
|
||||
}
|
||||
$patches = [
|
||||
'file' => $service->getDownloadFileUrl( $file ),
|
||||
'name' => $file->entry->title,
|
||||
'outputName' => $file->filename
|
||||
];
|
||||
|
||||
return view('tools.play', compact('patches', 'emuConfig'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,23 @@ class EntryFile extends Model
|
||||
return $this->hasOne(PlayOnlineSetting::class,'file_id');
|
||||
}
|
||||
|
||||
public function prettyFileSize(): string
|
||||
{
|
||||
$bytes = $this->filesize;
|
||||
|
||||
if ($bytes >= 1073741824) {
|
||||
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
|
||||
} elseif ($bytes >= 1048576) {
|
||||
$bytes = number_format($bytes / 1048576, 2) . ' MB';
|
||||
} elseif ($bytes >= 1024) {
|
||||
$bytes = number_format($bytes / 1024, 2) . ' KB';
|
||||
} else {
|
||||
$bytes = ($bytes == 0) ? '0 bytes' : $bytes . ' bytes';
|
||||
}
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
public function increaseDownloadCount(): void
|
||||
{
|
||||
$this->download_count++;
|
||||
|
||||
@@ -8,6 +8,8 @@ use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FileServersService {
|
||||
|
||||
@@ -69,15 +71,38 @@ class FileServersService {
|
||||
* @param EntryFile $file
|
||||
* @return string
|
||||
*/
|
||||
public function getDownloadFileUrl( EntryFile $file ): string
|
||||
public function getDownloadFileUrl( EntryFile $file, bool $countDownload = true ): string
|
||||
{
|
||||
$serverKey = $this->getEntryFileServerKey( $file );
|
||||
$url = $this->servers[$serverKey]['download'] ?? "#";
|
||||
if( $url === "#" )
|
||||
return $url;
|
||||
|
||||
return $url . "&" . http_build_query( [ 'filename' => $file->filename, 'filepath' => $file->filepath ] );
|
||||
$args = [ 'filename' => $file->filename, 'filepath' => $file->filepath ];
|
||||
if( !$countDownload )
|
||||
$args['count_download'] = false;
|
||||
|
||||
return $url . "&" . http_build_query( $args );
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function getArchiveExplorerUrl( EntryFile $file ): ?string
|
||||
{
|
||||
if( !Str::endsWith( $file->filename, ['zip', 'rar', '7z'] ) )
|
||||
return null;
|
||||
|
||||
$serverKey = $this->getEntryFileServerKey( $file );
|
||||
$url = $this->servers[$serverKey]['file_explorer'] ?? "#";
|
||||
|
||||
if( $url === "#" )
|
||||
return null;
|
||||
|
||||
$args = [ 'filename' => $file->filename, 'filepath' => $file->filepath,
|
||||
'zeus' => $this->generateZeusToken(\Auth::user()->user_id ?? 0, $this->servers[$serverKey]['base_url'], 'Fileexplorer')
|
||||
];
|
||||
|
||||
return $url . "&" . http_build_query( $args );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,11 +162,13 @@ class FileServersService {
|
||||
|
||||
foreach( $this->servers as $serverKey => $server ){
|
||||
|
||||
$response = Http::withHeaders([])
|
||||
$token = $this->generateZeusToken( $userId, $server['base_url'], "Deletefile" );
|
||||
|
||||
$response = Http::asForm()->withHeaders([])
|
||||
->post( $server['delete_file'], [
|
||||
'filepath' => $filePath,
|
||||
'filename' => $fileName,
|
||||
'zeus' => $this->generateZeusToken( $userId, $server['base_url'], "Deletefile" ),
|
||||
'zeus' => $token,
|
||||
]);
|
||||
|
||||
if (!$response->successful()) {
|
||||
|
||||
@@ -63,8 +63,10 @@ class SubmissionsService {
|
||||
if( $files === [] )
|
||||
return [];
|
||||
|
||||
$service = app(FileServersService::class);
|
||||
|
||||
return array_map(
|
||||
function( string $uuid ) {
|
||||
function( string $uuid ) use ($service) {
|
||||
$file = EntryFile::where('file_uuid', $uuid)->first();
|
||||
|
||||
if( $file )
|
||||
@@ -78,6 +80,9 @@ class SubmissionsService {
|
||||
'error' => null,
|
||||
'uuid' => $uuid,
|
||||
'state' => $file->state,
|
||||
'file_explorer' => $service->getArchiveExplorerUrl($file),
|
||||
'file_explorer_files' => null,
|
||||
'download_url' => $service->getDownloadFileUrl($file, false),
|
||||
'can_be_online_patched' => EntryHelpers::enableOnlinePatcherBasedOnExtension($file['filename']),
|
||||
'meta_online_patcher' => $file->online_patcher,
|
||||
'meta_secondary_online_patcher' => $file->secondary_online_patcher,
|
||||
@@ -98,6 +103,9 @@ class SubmissionsService {
|
||||
'error' => null,
|
||||
'uuid' => $uuid,
|
||||
'state' => $file['state'],
|
||||
'file_explorer' => null,
|
||||
'file_explorer_files' => null,
|
||||
'download_url' => null,
|
||||
'can_be_online_patched' => EntryHelpers::enableOnlinePatcherBasedOnExtension($file['filename']),
|
||||
'meta_online_patcher' => false,
|
||||
'meta_secondary_online_patcher' => false,
|
||||
@@ -707,10 +715,10 @@ class SubmissionsService {
|
||||
$needDeletion = array_diff( $existingUuids, $requestUuids );
|
||||
if( !empty( $needDeletion ) ){
|
||||
$userId = \Auth::user()->user_id;
|
||||
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->get()->each( function ( $f ) use ( $userId ) {
|
||||
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->whereNot('state', 'archived')->get()->each( function ( $f ) use ( $userId ) {
|
||||
DeleteFile::dispatch( $f->filepath, $f->filename, $userId);
|
||||
});
|
||||
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->delete();
|
||||
EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->whereNot('state', 'archived')->delete();
|
||||
}
|
||||
|
||||
$needAddition = array_diff( $requestUuids, $existingUuids );
|
||||
|
||||
43
resources/js/PlayOnline.js
Normal file
43
resources/js/PlayOnline.js
Normal file
@@ -0,0 +1,43 @@
|
||||
window.PlayOnline = function( filePath = "", emulatorJsConfig = {} ){
|
||||
|
||||
return {
|
||||
|
||||
fileUrl: filePath,
|
||||
emuConfig: emulatorJsConfig,
|
||||
|
||||
init(){
|
||||
this.launchEmulatorJs();
|
||||
},
|
||||
|
||||
cleanEmulatorJsVars() {
|
||||
['EJS_player','EJS_core','EJS_gameUrl','EJS_pathtodata',
|
||||
'EJS_startOnLoaded','EJS_threads']
|
||||
.forEach(k => delete window[k]);
|
||||
},
|
||||
|
||||
prepareEmulatorJs(){
|
||||
window.EJS_player = '#game';
|
||||
window.EJS_core = this.emuConfig.core;
|
||||
window.EJS_gameUrl = this.filePath;
|
||||
window.EJS_pathtodata = "https://cdn.emulatorjs.org/stable/data/";
|
||||
window.EJS_startOnLoaded = true;
|
||||
window.EJS_threads = this.emuConfig.threads ?? false;
|
||||
},
|
||||
|
||||
launchEmulatorJs(){
|
||||
|
||||
this.cleanEmulatorJsVars();
|
||||
this.prepareEmulatorJs();
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.id = 'ejs-loader';
|
||||
script.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
||||
document.body.appendChild(script);
|
||||
|
||||
this.launchGame = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @typedef { import('types/UploadchunkResponse.js').UploadchunkResponse} UploadchunkResponse */
|
||||
|
||||
export const CHUNK_SIZE = 8192;
|
||||
export const CHUNK_SIZE = 8192 * 1024;
|
||||
|
||||
const PATCH_EXTENSIONS = new Set([
|
||||
'ips', 'bps', 'ups', 'aps', 'ppf', 'xdelta', "zip"
|
||||
@@ -72,6 +72,15 @@ export function FSFileData(name, totalChunks, rawFile ) {
|
||||
*/
|
||||
state: 'public',
|
||||
|
||||
file_explorer: null,
|
||||
|
||||
file_explorer_files: null,
|
||||
|
||||
/**
|
||||
* For files already uploaded, download URL.
|
||||
*/
|
||||
download_url: null,
|
||||
|
||||
can_be_online_patched: PATCH_EXTENSIONS.has(extension),
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,6 +105,29 @@ export function FSUploader(){
|
||||
|
||||
},
|
||||
|
||||
handleDownloadFile( index ){
|
||||
let download_url = this.files[index].download_url;
|
||||
window.location.href = download_url;
|
||||
},
|
||||
|
||||
async handleFileExplorer( index ){
|
||||
|
||||
if( this.files[index].file_explorer_files !== null )
|
||||
return;
|
||||
|
||||
let file_explorer_url = this.files[index].file_explorer;
|
||||
|
||||
let response = await fetch(file_explorer_url, { method: 'GET', headers: { 'Content-Type': 'application/json' } });
|
||||
let json = await response.json();
|
||||
|
||||
if( !json.files ){
|
||||
this.files[index].file_explorer_files = [ "An error occurred during request" ];
|
||||
return;
|
||||
}
|
||||
|
||||
this.files[index].file_explorer_files = json.files;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retry file uploading.
|
||||
*
|
||||
@@ -126,6 +149,8 @@ export function FSUploader(){
|
||||
* @param {number} index FSFileData index in this.files.
|
||||
*/
|
||||
handleRemoveFile( index ){
|
||||
if( this.files[index].state === 'archived')
|
||||
return;
|
||||
this.files.splice(index, 1);
|
||||
},
|
||||
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
<div class="modal-body">
|
||||
<div class="file-list">
|
||||
@forelse( $files as $file )
|
||||
@if( $file->state === 'private' )
|
||||
@continue
|
||||
@endif
|
||||
<div class="file-item">
|
||||
<div class="file-info">
|
||||
<span class="file-name">{{ $file->filename }}</span>
|
||||
<span class="file-meta">{{ $file->filesize }} - {{ $file->download_count }} downloads</span>
|
||||
<span class="file-name">@if($file->state === 'archived')<i data-lucide="archive"></i> @endif{{ $file->filename }}</span>
|
||||
<span class="file-meta">{{ $file->prettyFileSize() }} - {{ $file->download_count }} downloads</span>
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;gap:15px;">
|
||||
<a href="{{ route('fs.download', ['entry_id' => $entryId, 'file' => $file->file_uuid] ) }}" class="btn primary"><i data-lucide="download"></i> Download</a>
|
||||
|
||||
@@ -100,7 +100,41 @@
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="upload-item-actions" x-data="{ showMetadata: false }">
|
||||
<div class="upload-item-actions" x-data="{ showMetadata: false, showFileExplorer: false }">
|
||||
<button type="button" class="btn" x-show="file.download_url" @click="handleDownloadFile(i)">
|
||||
<i data-lucide="download"></i>
|
||||
</button>
|
||||
<button type="button" class="btn" x-show="file.file_explorer" @click="handleFileExplorer(i); showFileExplorer = true;">
|
||||
<i data-lucide="package-open"></i>
|
||||
</button>
|
||||
<template x-if="file.file_explorer" x-teleport="body">
|
||||
<div class="modal-overlay"
|
||||
x-cloak
|
||||
x-show="showFileExplorer"
|
||||
x-transition.opacity.duration.300ms
|
||||
@click.self="showFileExplorer = false"
|
||||
@keydown.escape.window="showFileExplorer = false">
|
||||
<div class="modal-window" x-show="showFileExplorer" x-transition>
|
||||
|
||||
<div class="modal-header">
|
||||
<span class="modal-title" style="display: flex; align-items: center; gap: 8px;">
|
||||
File explorer: <span x-text="file.name" style="color: var(--rhpz-orange);"></span>
|
||||
</span>
|
||||
<button type="button" class="modal-close" @click="showFileExplorer = false">
|
||||
<i data-lucide="x" size="20"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-content">
|
||||
<ul>
|
||||
<template x-for="(f,j) in file.file_explorer_files" :key="j">
|
||||
<li><span x-text="f"></span></li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<button type="button" class="btn" x-show="file.error" @click="handleRetryFile(i)">
|
||||
<i data-lucide="refresh-cw"></i>
|
||||
</button>
|
||||
@@ -109,7 +143,7 @@
|
||||
<i data-lucide="settings"></i>
|
||||
</button>
|
||||
@endif
|
||||
<button type="button" class="btn" x-show="file.done || file.error" @click="handleRemoveFile(i)">
|
||||
<button type="button" class="btn" x-show="(file.done || file.error) && file.state !== 'archived'" @click="handleRemoveFile(i)">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
<template x-teleport="body">
|
||||
|
||||
17
resources/views/tools/play-homebrew.blade.php
Normal file
17
resources/views/tools/play-homebrew.blade.php
Normal file
@@ -0,0 +1,17 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('page-title', "Play Online - " . config('app.name'))
|
||||
|
||||
@push('scripts')
|
||||
@vite('resources/js/PlayOnline.js')
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="page-title">
|
||||
<span>Play Online</span>
|
||||
</div>
|
||||
|
||||
<div id="rom-patcher-container" class="patcher-container" x-data="PlayOnline({{ Js::from($filePath) }}, {{ JS::from($emuConfig ?? [])}})">
|
||||
<div id="game"></div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -8,7 +8,8 @@ export default defineConfig({
|
||||
laravel({
|
||||
input: ['resources/css/app.css', 'resources/js/app.js',
|
||||
'resources/js/submissions.js', 'resources/js/news-submissions.js',
|
||||
'resources/js/RomPatcher.js', 'resources/js/PlayOnlineAndPatcher.js'
|
||||
'resources/js/RomPatcher.js', 'resources/js/PlayOnlineAndPatcher.js',
|
||||
'resources/js/PlayOnline.js'
|
||||
],
|
||||
refresh: true,
|
||||
fonts: [
|
||||
|
||||
Reference in New Issue
Block a user