Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a0fd282b7 |
@@ -10,7 +10,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/RarDog/Lunaris/releases"><img src="https://img.shields.io/badge/Version-3.1.3-8A2BE2?style=for-the-badge&logo=semver&logoColor=white" alt="Version 3.1.3" /></a>
|
<a href="https://github.com/RarDog/Lunaris/releases"><img src="https://img.shields.io/badge/Version-3.1.4-8A2BE2?style=for-the-badge&logo=semver&logoColor=white" alt="Version 3.1.4" /></a>
|
||||||
<img src="https://img.shields.io/badge/Flutter-3.47+-02569B?style=for-the-badge&logo=flutter&logoColor=white" alt="Flutter" />
|
<img src="https://img.shields.io/badge/Flutter-3.47+-02569B?style=for-the-badge&logo=flutter&logoColor=white" alt="Flutter" />
|
||||||
<img src="https://img.shields.io/badge/Platform-Android%20%7C%20Linux%20%7C%20Windows-00C853?style=for-the-badge" alt="Platforms" />
|
<img src="https://img.shields.io/badge/Platform-Android%20%7C%20Linux%20%7C%20Windows-00C853?style=for-the-badge" alt="Platforms" />
|
||||||
<a href="https://github.com/RarDog/Lunaris/wiki"><img src="https://img.shields.io/badge/Wiki-Documentation-FF6F00?style=for-the-badge&logo=gitbook&logoColor=white" alt="Wiki" /></a>
|
<a href="https://github.com/RarDog/Lunaris/wiki"><img src="https://img.shields.io/badge/Wiki-Documentation-FF6F00?style=for-the-badge&logo=gitbook&logoColor=white" alt="Wiki" /></a>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
const appDisplayVersion = '3.1.3';
|
const appDisplayVersion = '3.1.4';
|
||||||
const appBuildNumber = 64;
|
const appBuildNumber = 65;
|
||||||
const appReleaseChannel = 'Stable';
|
const appReleaseChannel = 'Stable';
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
const lunarisChangelog = [
|
const lunarisChangelog = [
|
||||||
|
LunarisChange(
|
||||||
|
version: '3.1.4',
|
||||||
|
title: 'Оптимизация видеоплеера — адаптивная высота и идеальное прилегание полосы воспроизведения',
|
||||||
|
bullets: [
|
||||||
|
'Устранение зазора под видео: контейнер плеера на мобильных устройствах и ПК теперь динамически подстраивается под пропорции видео (aspect ratio) вместо жестко фиксированной высоты 62% экрана, устраняя огромные пустые черные полосы сверху и снизу.',
|
||||||
|
'Точное позиционирование полосы воспроизведения: исправлен отступ снизу и ложное наложение системных insets на неполноэкранный плеер в карточке поста — ползунок воспроизведения, таймкоды и кнопка фуллскрина теперь аккуратно прилегают к нижнему краю видео без зависания в воздухе.',
|
||||||
|
'Компактные элементы управления: выровнены буферный трек и слайдер прокрутки, оптимизирован градиент затенения для максимальной видимости и удобства управления.',
|
||||||
|
],
|
||||||
|
),
|
||||||
LunarisChange(
|
LunarisChange(
|
||||||
version: '3.1.3',
|
version: '3.1.3',
|
||||||
title: 'iPhone Liquid Glass — Источники, Проверка сети и Навигация назад по вкладкам',
|
title: 'iPhone Liquid Glass — Источники, Проверка сети и Навигация назад по вкладкам',
|
||||||
|
|||||||
@@ -219,34 +219,71 @@ class PostDetailsScreen extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SizedBox(
|
child: MediaUrlSelector.isVideo(post)
|
||||||
width: double.infinity,
|
? ConstrainedBox(
|
||||||
height: 760,
|
constraints: const BoxConstraints(
|
||||||
child: PostMediaViewer(
|
maxHeight: 760,
|
||||||
key: ValueKey(post.cacheKey),
|
),
|
||||||
post: post,
|
child: AspectRatio(
|
||||||
localFilePath: localMedia?.savedPath,
|
aspectRatio:
|
||||||
qualityMode: qualityMode,
|
(post.width > 0 && post.height > 0)
|
||||||
mediaHeaders: ref
|
? (post.width / post.height)
|
||||||
.watch(postMediaHeadersProvider(post))
|
.clamp(0.45, 2.4)
|
||||||
.value ??
|
: (16 / 9),
|
||||||
const {},
|
child: PostMediaViewer(
|
||||||
initialPosition: Duration(
|
key: ValueKey(post.cacheKey),
|
||||||
milliseconds: settings.videoPlaybackPositions[
|
post: post,
|
||||||
post.cacheKey] ??
|
localFilePath: localMedia?.savedPath,
|
||||||
0,
|
qualityMode: qualityMode,
|
||||||
),
|
mediaHeaders: ref
|
||||||
initialLoop: settings.videoPlayerLoop,
|
.watch(postMediaHeadersProvider(post))
|
||||||
initialMuted: settings.videoPlayerMuted,
|
.value ??
|
||||||
initialCoverVideo: settings.videoPlayerCover,
|
const {},
|
||||||
initialHalfVolume:
|
initialPosition: Duration(
|
||||||
settings.videoPlayerHalfVolume,
|
milliseconds: settings.videoPlaybackPositions[
|
||||||
onPlaybackSnapshot: (snapshot) =>
|
post.cacheKey] ??
|
||||||
_saveVideoSnapshot(ref, post, snapshot),
|
0,
|
||||||
onPlaybackPreferencesChanged: (snapshot) =>
|
),
|
||||||
_saveVideoPreferences(ref, snapshot),
|
initialLoop: settings.videoPlayerLoop,
|
||||||
),
|
initialMuted: settings.videoPlayerMuted,
|
||||||
),
|
initialCoverVideo: settings.videoPlayerCover,
|
||||||
|
initialHalfVolume:
|
||||||
|
settings.videoPlayerHalfVolume,
|
||||||
|
onPlaybackSnapshot: (snapshot) =>
|
||||||
|
_saveVideoSnapshot(ref, post, snapshot),
|
||||||
|
onPlaybackPreferencesChanged: (snapshot) =>
|
||||||
|
_saveVideoPreferences(ref, snapshot),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 760,
|
||||||
|
child: PostMediaViewer(
|
||||||
|
key: ValueKey(post.cacheKey),
|
||||||
|
post: post,
|
||||||
|
localFilePath: localMedia?.savedPath,
|
||||||
|
qualityMode: qualityMode,
|
||||||
|
mediaHeaders: ref
|
||||||
|
.watch(postMediaHeadersProvider(post))
|
||||||
|
.value ??
|
||||||
|
const {},
|
||||||
|
initialPosition: Duration(
|
||||||
|
milliseconds: settings.videoPlaybackPositions[
|
||||||
|
post.cacheKey] ??
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
initialLoop: settings.videoPlayerLoop,
|
||||||
|
initialMuted: settings.videoPlayerMuted,
|
||||||
|
initialCoverVideo: settings.videoPlayerCover,
|
||||||
|
initialHalfVolume:
|
||||||
|
settings.videoPlayerHalfVolume,
|
||||||
|
onPlaybackSnapshot: (snapshot) =>
|
||||||
|
_saveVideoSnapshot(ref, post, snapshot),
|
||||||
|
onPlaybackPreferencesChanged: (snapshot) =>
|
||||||
|
_saveVideoPreferences(ref, snapshot),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
@@ -409,33 +446,72 @@ class PostDetailsScreen extends ConsumerWidget {
|
|||||||
? null
|
? null
|
||||||
: () =>
|
: () =>
|
||||||
_showMobileQuickActions(context, ref, post, favoriteKeys),
|
_showMobileQuickActions(context, ref, post, favoriteKeys),
|
||||||
child: SizedBox(
|
child: isVideo
|
||||||
width: double.infinity,
|
? Center(
|
||||||
height: MediaQuery.sizeOf(context).height * 0.62,
|
child: ConstrainedBox(
|
||||||
child: PostMediaViewer(
|
constraints: BoxConstraints(
|
||||||
key: ValueKey(post.cacheKey),
|
maxHeight: MediaQuery.sizeOf(context).height * 0.70,
|
||||||
post: post,
|
),
|
||||||
localFilePath: localMedia?.savedPath,
|
child: AspectRatio(
|
||||||
qualityMode: qualityMode,
|
aspectRatio: (post.width > 0 && post.height > 0)
|
||||||
mediaHeaders:
|
? (post.width / post.height).clamp(0.45, 2.4)
|
||||||
ref.watch(postMediaHeadersProvider(post)).value ?? const {},
|
: (16 / 9),
|
||||||
initialPosition: Duration(
|
child: PostMediaViewer(
|
||||||
milliseconds:
|
key: ValueKey(post.cacheKey),
|
||||||
settings.videoPlaybackPositions[post.cacheKey] ?? 0,
|
post: post,
|
||||||
),
|
localFilePath: localMedia?.savedPath,
|
||||||
initialLoop: settings.videoPlayerLoop,
|
qualityMode: qualityMode,
|
||||||
initialMuted: settings.videoPlayerMuted,
|
mediaHeaders:
|
||||||
initialCoverVideo: settings.videoPlayerCover,
|
ref.watch(postMediaHeadersProvider(post)).value ??
|
||||||
initialHalfVolume: settings.videoPlayerHalfVolume,
|
const {},
|
||||||
onPlaybackSnapshot: (snapshot) =>
|
initialPosition: Duration(
|
||||||
_saveVideoSnapshot(ref, post, snapshot),
|
milliseconds:
|
||||||
onPlaybackPreferencesChanged: (snapshot) =>
|
settings.videoPlaybackPositions[post.cacheKey] ??
|
||||||
_saveVideoPreferences(ref, snapshot),
|
0,
|
||||||
onMediaGestureLockChanged: (locked) {
|
),
|
||||||
onMediaGestureLockChanged?.call(locked);
|
initialLoop: settings.videoPlayerLoop,
|
||||||
},
|
initialMuted: settings.videoPlayerMuted,
|
||||||
),
|
initialCoverVideo: settings.videoPlayerCover,
|
||||||
),
|
initialHalfVolume: settings.videoPlayerHalfVolume,
|
||||||
|
onPlaybackSnapshot: (snapshot) =>
|
||||||
|
_saveVideoSnapshot(ref, post, snapshot),
|
||||||
|
onPlaybackPreferencesChanged: (snapshot) =>
|
||||||
|
_saveVideoPreferences(ref, snapshot),
|
||||||
|
onMediaGestureLockChanged: (locked) {
|
||||||
|
onMediaGestureLockChanged?.call(locked);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: MediaQuery.sizeOf(context).height * 0.62,
|
||||||
|
child: PostMediaViewer(
|
||||||
|
key: ValueKey(post.cacheKey),
|
||||||
|
post: post,
|
||||||
|
localFilePath: localMedia?.savedPath,
|
||||||
|
qualityMode: qualityMode,
|
||||||
|
mediaHeaders:
|
||||||
|
ref.watch(postMediaHeadersProvider(post)).value ??
|
||||||
|
const {},
|
||||||
|
initialPosition: Duration(
|
||||||
|
milliseconds:
|
||||||
|
settings.videoPlaybackPositions[post.cacheKey] ?? 0,
|
||||||
|
),
|
||||||
|
initialLoop: settings.videoPlayerLoop,
|
||||||
|
initialMuted: settings.videoPlayerMuted,
|
||||||
|
initialCoverVideo: settings.videoPlayerCover,
|
||||||
|
initialHalfVolume: settings.videoPlayerHalfVolume,
|
||||||
|
onPlaybackSnapshot: (snapshot) =>
|
||||||
|
_saveVideoSnapshot(ref, post, snapshot),
|
||||||
|
onPlaybackPreferencesChanged: (snapshot) =>
|
||||||
|
_saveVideoPreferences(ref, snapshot),
|
||||||
|
onMediaGestureLockChanged: (locked) {
|
||||||
|
onMediaGestureLockChanged?.call(locked);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
PostActionBar(
|
PostActionBar(
|
||||||
|
|||||||
@@ -1792,7 +1792,10 @@ class _VideoControls extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
top: fullscreen,
|
||||||
bottom: false,
|
bottom: false,
|
||||||
|
left: fullscreen,
|
||||||
|
right: fullscreen,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (fullscreen) ...[
|
if (fullscreen) ...[
|
||||||
@@ -1910,7 +1913,12 @@ class _VideoControls extends StatelessWidget {
|
|||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.fromLTRB(14, 12, 14, 10),
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
14,
|
||||||
|
8,
|
||||||
|
14,
|
||||||
|
fullscreen ? 10 : 4,
|
||||||
|
),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.bottomCenter,
|
begin: Alignment.bottomCenter,
|
||||||
@@ -1920,6 +1928,9 @@ class _VideoControls extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
|
bottom: fullscreen,
|
||||||
|
left: fullscreen,
|
||||||
|
right: fullscreen,
|
||||||
child: StreamBuilder<Duration>(
|
child: StreamBuilder<Duration>(
|
||||||
stream: player.stream.duration,
|
stream: player.stream.duration,
|
||||||
initialData: player.state.duration,
|
initialData: player.state.duration,
|
||||||
@@ -1967,8 +1978,8 @@ class _VideoControls extends StatelessWidget {
|
|||||||
// Buffer progress track
|
// Buffer progress track
|
||||||
if (maxMs > 0 && bufferMs > 0)
|
if (maxMs > 0 && bufferMs > 0)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 20,
|
left: 14,
|
||||||
right: 20,
|
right: 14,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(2),
|
BorderRadius.circular(2),
|
||||||
@@ -1976,10 +1987,12 @@ class _VideoControls extends StatelessWidget {
|
|||||||
value: (bufferMs / maxMs)
|
value: (bufferMs / maxMs)
|
||||||
.clamp(0.0, 1.0),
|
.clamp(0.0, 1.0),
|
||||||
minHeight: 3.5,
|
minHeight: 3.5,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor:
|
||||||
|
Colors.transparent,
|
||||||
valueColor:
|
valueColor:
|
||||||
AlwaysStoppedAnimation(
|
AlwaysStoppedAnimation(
|
||||||
Colors.white.withValues(alpha: 0.28),
|
Colors.white
|
||||||
|
.withValues(alpha: 0.28),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1997,6 +2010,10 @@ class _VideoControls extends StatelessWidget {
|
|||||||
const RoundSliderThumbShape(
|
const RoundSliderThumbShape(
|
||||||
enabledThumbRadius: 6,
|
enabledThumbRadius: 6,
|
||||||
),
|
),
|
||||||
|
overlayShape:
|
||||||
|
const RoundSliderOverlayShape(
|
||||||
|
overlayRadius: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Slider(
|
child: Slider(
|
||||||
value: valueMs,
|
value: valueMs,
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
name: gel_rule_app
|
name: gel_rule_app
|
||||||
description: Local backend/core layer for a Flutter booru browser app.
|
description: Local backend/core layer for a Flutter booru browser app.
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 3.1.3+64
|
version: 3.1.4+65
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.0 <4.0.0"
|
sdk: ">=3.3.0 <4.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user