Skip to content

Commit 1dcc84f

Browse files
committed
feat: features added & changes
Added - Style Switcher - Classic Style (Default) - Liquid Glass Style (EXPERIMENTAL) - Theme Switcher - Dark Theme - Light Theme - Keyboard Shortcut - Space (Extract) - Esc (Cancel Extraction) - Ctrl+L (Open Log Panel) - Ctrl+S (Open Settings Panel) Changes - Log & Settings now show as dialog in desktop - Splash Screen now following current theme & style
1 parent a933d9b commit 1dcc84f

12 files changed

Lines changed: 3273 additions & 948 deletions

File tree

.github/workflows/build.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -488,26 +488,19 @@ jobs:
488488
with:
489489
name: "Frame Extractor ${{ github.ref_name }}"
490490
body: |
491-
![latest download](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/total?style=flat-square&color=3258a8)
492491
${{ env.CHANGELOG }}
493492
494493
---
495494
496495
## Downloads
496+
### Windwos
497+
[![Windows-Installer](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-windows-installer.exe?displayAssetName=true&style=flat-square&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVR4nO3RgQkAIRDAsNt%2FaX8HebBKMkGhMwAAADErbi7vNeQwQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMeW0IAOxacXN5ryGHGRJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjyGtDAAAA5l8fzPuhzmfqIykAAAAASUVORK5CYII%3D&label=%20&color=blue)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-windows-installer.exe) [![Windows-Portable](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-windows-portable.zip?displayAssetName=true&style=flat-square&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVR4nO3RgQkAIRDAsNt%2FaX8HebBKMkGhMwAAADErbi7vNeQwQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMiTEkxpAYQ2IMeW0IAOxacXN5ryGHGRJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjSIwhMYbEGBJjyGtDAAAA5l8fzPuhzmfqIykAAAAASUVORK5CYII%3D&label=%20&color=blue) ](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-windows-portable.zip)
497498
498-
| Platform | File | Notes |
499-
|---|---|---|
500-
| 🪟 Windows | `FrameExtractor-windows-installer.zip` | Extract & run `.exe` |
501-
| 🪟 Windows Portable | `FrameExtractor-windows-portable.zip` | No install needed |
502-
| 🐧 Linux (.deb) | `FrameExtractor-linux-installer.deb` | `sudo dpkg -i *.deb` - installs to `/opt/frameextractor` |
503-
| 🐧 Linux (.AppImage) | `FrameExtractor-linux.AppImage` | Single file, runs on any distro |
504-
| 🐧 Linux Portable | `FrameExtractor-linux-portable.tar.gz` | Extract & run anywhere |
505-
| 🤖 Android (arm64) | `FrameExtractor-android-arm64.apk` | Most modern phones (2018+) |
506-
| 🤖 Android (arm32) | `FrameExtractor-android-arm32.apk` | Older 32-bit phones |
507-
| 🤖 Android (x86_64) | `FrameExtractor-android-x86_64.apk` | Emulators / Chromebooks |
508-
509-
> **Windows/Linux:** ffmpeg & yt-dlp bundled inside the zip/dmg.
510-
> **Android:** yt-dlp bundled in APK, ffmpeg via ffmpeg_kit.
499+
### Linux
500+
[![Linux-Debian](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-linux-installer.deb?displayAssetName=true&style=flat-square&logo=linux&logoColor=white&label=%20&color=e07334)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-linux-installer.deb) [![Linux-AppImage](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-linux.AppImage?displayAssetName=true&style=flat-square&logo=linux&logoColor=white&label=%20&color=e07334)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-linux.AppImage) [![Linux-Portable](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-linux-portable.tar.gz?displayAssetName=true&style=flat-square&logo=linux&logoColor=white&label=%20&color=e07334)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-linux-portable.tar.gz)
501+
502+
### Android
503+
[![Android-ARM64](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-android-arm64.apk?displayAssetName=true&style=flat-square&logo=android&logoColor=white&label=%20&color=dark-green)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-android-arm64.apk) [![Android-ARM32](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-android-arm32.apk?displayAssetName=true&style=flat-square&logo=android&logoColor=white&label=%20&color=dark-green)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-android-arm32.apk) [![Android-X86_64](https://img.shields.io/github/downloads/nokarin-dev/frameextractor/${{ github.ref_name }}/FrameExtractor-android-x86_64.apk?displayAssetName=true&style=flat-square&logo=android&logoColor=white&label=%20&color=dark-green)](https://github.com/nokarin-dev/FrameExtractor/releases/${{ github.ref_name }}/download/FrameExtractor-android-x86_64.apk)
511504
draft: false
512505
prerelease: ${{ contains(github.ref_name, '-') }}
513506
files: |

changelog.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,24 @@ This project loosely follows Keep a Changelog and uses Semantic Versioning.
1212
- Fixed Linux AppImage launch error `Is a directory` - `$EXE_NAME` was set in CI shell but not expanded inside the AppRun heredoc (single-quoted heredoc prevents variable substitution). AppRun now detects the executable at runtime using `find`
1313

1414
#### Added
15+
- Style Switcher
16+
- Classic Style (Default)
17+
- Liquid Glass Style (EXPERIMENTAL)
18+
- Theme Switcher
19+
- Dark Theme
20+
- Light Theme
21+
- Keyboard Shortcut
22+
- Space (Extract)
23+
- Esc (Cancel Extraction)
24+
- Ctrl+L (Open Log Panel)
25+
- Ctrl+S (Open Settings Panel)
1526
- Application Icons
1627

1728
### Changes
1829
#### Android
1930
- Upgrade android plugins to latest version
31+
- Log & Settings now show as dialog in desktop
32+
- Splash Screen now following current theme & style
2033

2134
---
2235

lib/core/app_constants.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
class AppConstants {
2+
// App info
3+
static const String appName = 'Frame Extractor';
4+
static const String appVersion = '1.1.2';
5+
static const String appDescription = 'Effortless video frame extraction';
6+
7+
// Extraction defaults
8+
static const int defaultFps = 30;
9+
static const int minFps = 1;
10+
static const int maxFps = 60;
11+
static const int defaultQuality = 90;
12+
static const int minQuality = 1;
13+
static const int maxQuality = 100;
14+
static const double defaultScale = 1.0;
15+
static const double minScale = 0.25;
16+
static const double maxScale = 2.0;
17+
static const String defaultStart = '00:00:00';
18+
static const String defaultEnd = '00:00:05';
19+
static const String defaultPrefix = 'frame_';
20+
static const String defaultFormat = 'jpg';
21+
22+
// Supported formats and video file types
23+
static const List<String> supportedFormats = ['png', 'jpg', 'webp', 'bmp'];
24+
static const List<String> supportedVideoExtensions = [
25+
'mp4',
26+
'mkv',
27+
'avi',
28+
'mov',
29+
'wmv',
30+
'flv',
31+
'webm',
32+
'm4v',
33+
'mpg',
34+
'mpeg',
35+
];
36+
37+
// Preferences
38+
static const String prefThemeMode = 'theme_mode';
39+
static const String prefUIStyle = 'ui_style';
40+
static const String prefLastOutput = 'last_output_dir';
41+
static const String prefRecentVideos = 'recent_videos';
42+
static const int maxRecentVideos = 8;
43+
44+
// Processing speed estimates
45+
static const double desktopSpeed = 150.0;
46+
static const double mobileSpeed = 50.0;
47+
}

lib/core/app_prefs.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'dart:convert';
2+
import 'package:shared_preferences/shared_preferences.dart';
3+
import 'package:frameextractor/core/app_constants.dart';
4+
5+
class AppPrefs {
6+
AppPrefs._();
7+
static late SharedPreferences _prefs;
8+
9+
static Future<void> init() async {
10+
_prefs = await SharedPreferences.getInstance();
11+
}
12+
13+
// Theme mode
14+
static String get themeMode =>
15+
_prefs.getString(AppConstants.prefThemeMode) ?? 'dark';
16+
static Future<void> setThemeMode(String v) =>
17+
_prefs.setString(AppConstants.prefThemeMode, v);
18+
19+
// UI style
20+
static String get uiStyle =>
21+
_prefs.getString(AppConstants.prefUIStyle) ?? 'classic';
22+
static Future<void> setUIStyle(String v) =>
23+
_prefs.setString(AppConstants.prefUIStyle, v);
24+
25+
// Last used output directory
26+
static String? get lastOutputDir =>
27+
_prefs.getString(AppConstants.prefLastOutput);
28+
static Future<void> setLastOutputDir(String v) =>
29+
_prefs.setString(AppConstants.prefLastOutput, v);
30+
31+
// Recent video files
32+
static List<String> get recentVideos {
33+
final raw = _prefs.getString(AppConstants.prefRecentVideos);
34+
if (raw == null) return [];
35+
try {
36+
return List<String>.from(jsonDecode(raw) as List);
37+
} catch (_) {
38+
return [];
39+
}
40+
}
41+
42+
static Future<void> addRecentVideo(String path) async {
43+
final list = recentVideos.where((p) => p != path).toList();
44+
list.insert(0, path);
45+
if (list.length > AppConstants.maxRecentVideos) {
46+
list.removeLast();
47+
}
48+
await _prefs.setString(AppConstants.prefRecentVideos, jsonEncode(list));
49+
}
50+
51+
static Future<void> clearRecentVideos() =>
52+
_prefs.remove(AppConstants.prefRecentVideos);
53+
}

lib/core/constants/app_constants.dart

Lines changed: 0 additions & 62 deletions
This file was deleted.

lib/core/theme/app_theme.dart

Lines changed: 0 additions & 67 deletions
This file was deleted.

lib/main.dart

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@ import 'package:flutter/material.dart';
33
import 'package:flutter_bloc/flutter_bloc.dart';
44
import 'package:window_manager/window_manager.dart';
55

6+
import 'package:frameextractor/core/app_constants.dart';
7+
import 'package:frameextractor/core/app_prefs.dart';
68
import 'package:frameextractor/core/binary_manager.dart';
79
import 'package:frameextractor/data/services/ffmpeg/ffmpeg_service.dart';
810
import 'package:frameextractor/data/services/youtube_service.dart';
911
import 'package:frameextractor/presentation/bloc/extraction_bloc.dart';
1012
import 'package:frameextractor/presentation/screens/home_screen.dart';
1113
import 'package:frameextractor/presentation/screens/splash_screen.dart';
14+
import 'package:frameextractor/presentation/theme/app_theme.dart';
1215

1316
const bool kIsPortable = bool.fromEnvironment('PORTABLE', defaultValue: false);
1417

1518
void main() async {
1619
WidgetsFlutterBinding.ensureInitialized();
1720

21+
await AppPrefs.init();
22+
1823
if (!Platform.isAndroid && !Platform.isIOS) {
1924
await windowManager.ensureInitialized();
2025
await windowManager.waitUntilReadyToShow(
2126
const WindowOptions(
22-
size: Size(820, 480),
23-
minimumSize: Size(640, 400),
27+
size: Size(1020, 640),
28+
minimumSize: Size(640, 420),
2429
center: true,
2530
titleBarStyle: TitleBarStyle.hidden,
2631
windowButtonVisibility: false,
27-
title: 'Frame Extractor',
32+
title: AppConstants.appName,
2833
),
2934
() async {
3035
await windowManager.show();
@@ -41,17 +46,20 @@ class App extends StatelessWidget {
4146

4247
@override
4348
Widget build(BuildContext context) {
44-
return MaterialApp(
45-
title: 'Frame Extractor',
46-
debugShowCheckedModeBanner: false,
47-
theme: ThemeData(
48-
colorScheme: const ColorScheme.dark(
49-
primary: Color(0xFF4F8EF7),
50-
surface: Color(0xFF13161E),
51-
),
52-
scaffoldBackgroundColor: const Color(0xFF0A0C10),
49+
return AppThemeProvider(
50+
initialThemeMode: AppPrefs.themeMode,
51+
initialUIStyle: AppPrefs.uiStyle,
52+
child: Builder(
53+
builder: (ctx) {
54+
final theme = AppTheme.of(ctx);
55+
return MaterialApp(
56+
title: AppConstants.appName,
57+
debugShowCheckedModeBanner: false,
58+
theme: theme.toMaterialTheme(),
59+
home: const _InitGate(),
60+
);
61+
},
5362
),
54-
home: const _InitGate(),
5563
);
5664
}
5765
}

0 commit comments

Comments
 (0)