Add NAS file manager integration and all pending platform changes
- Installed p7h/nas-file-manager package via private VCS repo - Published config/nas-file-manager.php with super_admin middleware restriction - Added NAS env vars to .env.example - Created admin/nas-storage page with connection info panel and file browser widget - Added NAS Storage link to admin sidebar (super_admin only) - Added SuperAdminController@nasStorage method and admin.nas-storage route - Includes all accumulated branch changes: profile wall, 2FA, audit logs, settings panel, country/phone/timezone components, posts, slideshow, playlist shares, video downloads/shares, comment likes, notifications, social links, and more Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
d44490dfe0
commit
0b2e95ea65
161 files changed
+27496
-7250
No files matched your search
@@ -4,14 +4,17 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Playlist;
|
||||
use App\Models\Video;
|
||||
use App\Services\GeoIpService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class PlaylistController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth')->except(['index', 'show', 'publicPlaylists', 'userPlaylists']);
|
||||
$this->middleware('auth')->except(['index', 'show', 'showByToken', 'publicPlaylists', 'userPlaylists', 'recordShare', 'accessShare']);
|
||||
}
|
||||
|
||||
// List user's playlists
|
||||
@@ -31,11 +34,100 @@ class PlaylistController extends Controller
|
||||
abort(404, 'Playlist not found');
|
||||
}
|
||||
|
||||
$videos = $playlist->videos()->orderBy('position')->paginate(20);
|
||||
$playlist->loadMissing('user');
|
||||
$videos = $playlist->videos()->with('user')->orderBy('position')->get();
|
||||
|
||||
return view('playlists.show', compact('playlist', 'videos'));
|
||||
}
|
||||
|
||||
// View playlist via unguessable share token (unlisted playlists)
|
||||
public function showByToken(string $token)
|
||||
{
|
||||
$playlist = Playlist::where('share_token', $token)->firstOrFail();
|
||||
|
||||
if (! $playlist->canViewViaToken(Auth::user())) {
|
||||
abort(404, 'Playlist not found');
|
||||
}
|
||||
|
||||
$playlist->loadMissing('user');
|
||||
$videos = $playlist->videos()->with('user')->orderBy('position')->get();
|
||||
|
||||
return view('playlists.show', compact('playlist', 'videos'));
|
||||
}
|
||||
|
||||
// Generate (or reuse) a per-user share tracking token and return the tracking URL
|
||||
public function recordShare(Playlist $playlist)
|
||||
{
|
||||
$userId = Auth::id();
|
||||
|
||||
if ($userId) {
|
||||
$existing = DB::table('playlist_shares')
|
||||
->where('playlist_id', $playlist->id)
|
||||
->where('user_id', $userId)
|
||||
->first();
|
||||
if ($existing) {
|
||||
return response()->json(['url' => route('playlists.accessShare', $existing->token)]);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$token = Str::random(10);
|
||||
} while (DB::table('playlist_shares')->where('token', $token)->exists());
|
||||
|
||||
DB::table('playlist_shares')->insert([
|
||||
'playlist_id' => $playlist->id,
|
||||
'user_id' => $userId,
|
||||
'token' => $token,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
return response()->json(['url' => route('playlists.accessShare', $token)]);
|
||||
}
|
||||
|
||||
// Handle a share link click: record the access, then redirect to the playlist
|
||||
public function accessShare(Request $request, string $token)
|
||||
{
|
||||
$share = DB::table('playlist_shares')->where('token', $token)->first();
|
||||
if (! $share) {
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
$playlist = Playlist::find($share->playlist_id);
|
||||
if (! $playlist || ! $playlist->canViewViaToken(Auth::user())) {
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
$did = $request->cookie('_did') ?: (string) Str::uuid();
|
||||
$seen = DB::table('playlist_share_accesses')
|
||||
->where('share_id', $share->id)
|
||||
->where('device_id', $did)
|
||||
->exists();
|
||||
|
||||
if (! $seen) {
|
||||
$ip = $request->header('CF-Connecting-IP')
|
||||
?? $request->header('X-Real-IP')
|
||||
?? $request->ip();
|
||||
$geo = GeoIpService::lookup($ip);
|
||||
|
||||
DB::table('playlist_share_accesses')->insert([
|
||||
'share_id' => $share->id,
|
||||
'device_id' => $did,
|
||||
'ip_address' => $ip,
|
||||
'country' => $geo['country'] ?? null,
|
||||
'country_name' => $geo['country_name'] ?? null,
|
||||
'accessed_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
$firstVideo = $playlist->videos()->orderBy('position')->first();
|
||||
$destination = $firstVideo
|
||||
? route('videos.show', $firstVideo) . '?playlist=' . $playlist->share_token
|
||||
: route('playlists.showByToken', $playlist->share_token);
|
||||
|
||||
return redirect($destination)
|
||||
->withCookie(cookie('_did', $did, 60 * 24 * 365 * 5));
|
||||
}
|
||||
|
||||
// Create new playlist form
|
||||
public function create()
|
||||
{
|
||||
@@ -48,8 +140,8 @@ class PlaylistController extends Controller
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:100',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'visibility' => 'nullable|in:public,private',
|
||||
'thumbnail' => 'nullable|image|mimes:jpeg,png,gif,webp|max:5120',
|
||||
'visibility' => 'nullable|in:public,private,unlisted',
|
||||
'thumbnail' => 'nullable|image|mimes:jpeg,png,gif,webp|max:20480',
|
||||
]);
|
||||
|
||||
$playlistData = [
|
||||
@@ -58,6 +150,7 @@ class PlaylistController extends Controller
|
||||
'description' => $request->description,
|
||||
'visibility' => $request->visibility ?? 'private',
|
||||
'is_default' => false,
|
||||
'share_token' => Str::random(32),
|
||||
];
|
||||
|
||||
// Create playlist first to get ID for thumbnail naming
|
||||
@@ -66,7 +159,7 @@ class PlaylistController extends Controller
|
||||
// Handle thumbnail upload
|
||||
if ($request->hasFile('thumbnail')) {
|
||||
$file = $request->file('thumbnail');
|
||||
$filename = 'playlist_'.$playlist->id.'_'.time().'.'.$file->getClientOriginalExtension();
|
||||
$filename = self::generateFilename($file->getClientOriginalExtension());
|
||||
$file->storeAs('public/thumbnails', $filename);
|
||||
$playlist->update(['thumbnail' => $filename]);
|
||||
}
|
||||
@@ -123,8 +216,8 @@ class PlaylistController extends Controller
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:100',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'visibility' => 'nullable|in:public,private',
|
||||
'thumbnail' => 'nullable|image|mimes:jpeg,png,gif,webp|max:5120',
|
||||
'visibility' => 'nullable|in:public,private,unlisted',
|
||||
'thumbnail' => 'nullable|image|mimes:jpeg,png,gif,webp|max:20480',
|
||||
]);
|
||||
|
||||
$updateData = [
|
||||
@@ -145,7 +238,7 @@ class PlaylistController extends Controller
|
||||
|
||||
// Upload new thumbnail
|
||||
$file = $request->file('thumbnail');
|
||||
$filename = 'playlist_'.$playlist->id.'_'.time().'.'.$file->getClientOriginalExtension();
|
||||
$filename = self::generateFilename($file->getClientOriginalExtension());
|
||||
$file->storeAs('public/thumbnails', $filename);
|
||||
$updateData['thumbnail'] = $filename;
|
||||
}
|
||||
@@ -351,7 +444,7 @@ class PlaylistController extends Controller
|
||||
// Play all videos in playlist (redirect to first video with playlist context)
|
||||
public function playAll(Playlist $playlist)
|
||||
{
|
||||
if (! $playlist->canView(Auth::user())) {
|
||||
if (! $playlist->canViewViaToken(Auth::user())) {
|
||||
abort(404, 'Playlist not found');
|
||||
}
|
||||
|
||||
@@ -361,17 +454,13 @@ class PlaylistController extends Controller
|
||||
return back()->with('error', 'Playlist is empty.');
|
||||
}
|
||||
|
||||
// Redirect to first video with playlist parameter
|
||||
return redirect()->route('videos.show', [
|
||||
'video' => $firstVideo->id,
|
||||
'playlist' => $playlist->id,
|
||||
]);
|
||||
return redirect(route('videos.show', $firstVideo) . '?playlist=' . $playlist->share_token);
|
||||
}
|
||||
|
||||
// Shuffle play - redirect to random video
|
||||
public function shuffle(Playlist $playlist)
|
||||
{
|
||||
if (! $playlist->canView(Auth::user())) {
|
||||
if (! $playlist->canViewViaToken(Auth::user())) {
|
||||
abort(404, 'Playlist not found');
|
||||
}
|
||||
|
||||
@@ -381,9 +470,6 @@ class PlaylistController extends Controller
|
||||
return back()->with('error', 'Playlist is empty.');
|
||||
}
|
||||
|
||||
return redirect()->route('videos.show', [
|
||||
'video' => $randomVideo->id,
|
||||
'playlist' => $playlist->id,
|
||||
]);
|
||||
return redirect(route('videos.show', $randomVideo) . '?playlist=' . $playlist->share_token);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user