From 9ae65670f0f2a46ad5be3949fd4daae2e925ce14 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Wed, 1 Jan 2025 21:33:04 +0100 Subject: [PATCH] Add Stats Page (#36) --- README.md | 35 +++---- app/Http/Controllers/StatController.php | 121 ++++++++++++++++++++++++ resources/js/Layouts/Layout.vue | 22 ++--- resources/js/Pages/Stats.vue | 56 +++++++++++ routes/web.php | 3 + 5 files changed, 206 insertions(+), 31 deletions(-) create mode 100644 app/Http/Controllers/StatController.php create mode 100644 resources/js/Pages/Stats.vue diff --git a/README.md b/README.md index 3ac6033..e176bda 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,12 @@ ## Table Of Contents -* [About the Project](#about-the-project) -* [Features](#features) -* [Contributing](#contributing) -* [License](#license) -* [Authors](#authors) -* [Acknowledgements](#acknowledgements) +- [About the Project](#about-the-project) +- [Features](#features) +- [Contributing](#contributing) +- [License](#license) +- [Authors](#authors) +- [Acknowledgements](#acknowledgements) ## About The Project @@ -35,17 +35,18 @@ videos into X264/AAC MP4 formats. The application also allows users to download audio from videos, download files from URLs, defining the maximum file size, and more. #### FFmpeg + The application uses FFmpeg to convert videos into the desired format and perform other operations on the videos. ## Features -- [X] Simple download of videos from various platforms and make them completely downloadable. (For private purposes) -- [X] Download target URLs -- [X] Convert videos to the desired format. - - [X] MP4 (H264/AAC) -- [X] Cut videos (From-to time) -- [X] Define maximum file size for videos by changing the bitrate -- [X] Collect statistics on converted videos +- [x] Simple download of videos from various platforms and make them completely downloadable. (For private purposes) +- [x] Download target URLs +- [x] Convert videos to the desired format. + - [x] MP4 (H264/AAC) +- [x] Cut videos (From-to time) +- [x] Define maximum file size for videos by changing the bitrate +- [x] Collect statistics on converted videos - [ ] Take subtitles from YouTube - [ ] Download only sound from videos. - [ ] Presets definable (So that a normal user only has to click on "Convert" and done) @@ -55,10 +56,10 @@ The application uses FFmpeg to convert videos into the desired format and perfor Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. -* If you have suggestions for adding or removing projects, feel free +- If you have suggestions for adding or removing projects, feel free to [open an issue](https://github.com/Tschucki/pr0verter/issues/new) to discuss it, or directly create a pull request. -* Create individual PR for each suggestion. -* Please also read through +- Create individual PR for each suggestion. +- Please also read through the [Code Of Conduct](https://github.com/Tschucki/pr0verter/blob/main/.github/CODE_OF_CONDUCT.md) before posting your first idea as well. @@ -77,4 +78,4 @@ information. ## Authors -* **[Tschucki](https://github.com/Tschucki)** - *Maintainer* +- **[Tschucki](https://github.com/Tschucki)** - _Maintainer_ diff --git a/app/Http/Controllers/StatController.php b/app/Http/Controllers/StatController.php new file mode 100644 index 0000000..240c3f7 --- /dev/null +++ b/app/Http/Controllers/StatController.php @@ -0,0 +1,121 @@ + $this->getStats(), + ]); + } + + private function getStats() + { + $stats = []; + + $allUrls = Statistic::whereNotNull('url')->get(); + $urls = null; + try { + $urls = $allUrls->groupBy(static function ($item) { + return parse_url($item->url, PHP_URL_HOST); + }); + + $synonyms = [ + [ + 'youtube.com', + 'www.youtube.com', + 'youtu.be', + 'm.youtube.com', + ], + ]; + + foreach ($synonyms as $synonym) { + $mainDomain = $synonym[0]; + $urls[$mainDomain] = $urls[$mainDomain] ?? collect(); + foreach ($synonym as $domain) { + if ($domain !== $mainDomain) { + $urls[$mainDomain] = $urls[$mainDomain]->merge($urls[$domain]); + unset($urls[$domain]); + } + } + } + + $urls = $urls->sortByDesc(fn ($item) => $item->count()); + } catch (Throwable $th) { + Log::error('Could not group urls by domain', ['exception' => $th]); + } + + $stats['favorite_url'] = [ + 'title' => 'Beliebteste Download-URL', + 'value' => $urls ? $urls->keys()->first() : 'Keine URLs vorhanden', + ]; + + $stats['currently_converting'] = [ + 'title' => 'Aktuell konvertierende Videos', + 'value' => Statistic::whereIn('status', [ConversionStatus::PROCESSING, ConversionStatus::PREPARING, ConversionStatus::DOWNLOADING])->where('created_at', '>', now()->subHour())->count(), + ]; + + $stats['uploaded_size'] = [ + 'title' => 'Traffic für hochgeladene Videos', + 'value' => Number::fileSize(Statistic::sum('size'), 2), + ]; + + $stats['extensions'] = [ + 'title' => 'Am häufigsten hochgeladene Dateiendung', + 'value' => Statistic::select('extension') + ->groupBy('extension') + ->orderByRaw('COUNT(extension) DESC') + ->first()->extension, + ]; + + $stats['finished'] = [ + 'title' => 'Erfolgreiche Konvertierungen', + 'value' => Statistic::where('status', ConversionStatus::FINISHED)->count(), + ]; + + $stats['average_conversion_time'] = [ + 'title' => 'Durchschnittliche Konvertierungszeit', + 'value' => Number::format(Statistic::where('status', ConversionStatus::FINISHED)->avg('conversion_time'), 2, locale: 'de-DE') . ' Sekunden', + ]; + + $stats['favorite_time_to_convert'] = [ + 'title' => 'Beliebteste Konvertierungszeit', + 'value' => Statistic::selectRaw('HOUR(created_at) as hour, COUNT(id) as count') + ->groupBy('hour') + ->orderByRaw('COUNT(id) DESC') + ->first()->hour . ' Uhr', + ]; + + $stats['added_watermarks'] = [ + 'title' => 'Wasserzeichen hinzugefügt', + 'value' => Statistic::where('watermark', true)->where('status', ConversionStatus::FINISHED)->count() . ' Wasserzeichen', + ]; + + $stats['auto_crop'] = [ + 'title' => 'Automatisch zugeschnittene Videos', + 'value' => Statistic::where('auto_crop', true)->where('status', ConversionStatus::FINISHED)->count() . ' Videos', + ]; + + $stats['trimmed'] = [ + 'title' => 'Videos zugeschnitten', + 'value' => Statistic::whereNotNull('trim_start')->orWhereNotNull('trim_end')->count() . ' Videos', + ]; + + $stats['removed_audio'] = [ + 'title' => 'Audio entfernt', + 'value' => Statistic::where('audio', false)->count() . ' mal', + ]; + + return $stats; + } +} diff --git a/resources/js/Layouts/Layout.vue b/resources/js/Layouts/Layout.vue index 73d83a2..2cb3bbb 100644 --- a/resources/js/Layouts/Layout.vue +++ b/resources/js/Layouts/Layout.vue @@ -88,29 +88,26 @@ const logout = async () => { - + - Konvertierungen + Statistik - + - Changelog + Konvertierungen - + { - + - Changelog + Statistik - + +import { Head } from '@inertiajs/vue3'; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from '@/components/ui/card/index.js'; + +defineProps({ + stats: { + type: Array, + required: true, + }, +}); + + + + + diff --git a/routes/web.php b/routes/web.php index d0bdbba..617a6b9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -9,6 +9,7 @@ use App\Http\Controllers\ListConverterController; use App\Http\Controllers\PrivacyPolicyController; use App\Http\Controllers\StartConverterController; +use App\Http\Controllers\StatController; use Illuminate\Support\Facades\Route; Route::get('/', HomeController::class)->name('home'); @@ -16,6 +17,8 @@ Route::get('/conversions', [ListConverterController::class, 'index']) ->name('conversions.list'); +Route::get('/stats', StatController::class)->name('stats'); + Route::post('/conversions', [ListConverterController::class, 'myConversions']) ->name('conversions.my'); Route::post('/converter/start', StartConverterController::class)