mirror of
https://github.com/immich-app/immich.git
synced 2025-07-08 18:54:18 -04:00
fix: handle remote asset orientation
This commit is contained in:
parent
0da5146e11
commit
4dbe2cc662
@ -23,6 +23,7 @@ class ExifInfo {
|
|||||||
String? state;
|
String? state;
|
||||||
String? country;
|
String? country;
|
||||||
String? description;
|
String? description;
|
||||||
|
String? orientation;
|
||||||
|
|
||||||
@ignore
|
@ignore
|
||||||
bool get hasCoordinates =>
|
bool get hasCoordinates =>
|
||||||
@ -45,6 +46,9 @@ class ExifInfo {
|
|||||||
@ignore
|
@ignore
|
||||||
String get focalLength => mm != null ? mm!.toStringAsFixed(1) : "";
|
String get focalLength => mm != null ? mm!.toStringAsFixed(1) : "";
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
bool get isFlipped => _isOrientationFlipped(orientation);
|
||||||
|
|
||||||
@ignore
|
@ignore
|
||||||
double? get latitude => lat;
|
double? get latitude => lat;
|
||||||
|
|
||||||
@ -67,7 +71,8 @@ class ExifInfo {
|
|||||||
city = dto.city,
|
city = dto.city,
|
||||||
state = dto.state,
|
state = dto.state,
|
||||||
country = dto.country,
|
country = dto.country,
|
||||||
description = dto.description;
|
description = dto.description,
|
||||||
|
orientation = dto.orientation;
|
||||||
|
|
||||||
ExifInfo({
|
ExifInfo({
|
||||||
this.id,
|
this.id,
|
||||||
@ -87,6 +92,7 @@ class ExifInfo {
|
|||||||
this.state,
|
this.state,
|
||||||
this.country,
|
this.country,
|
||||||
this.description,
|
this.description,
|
||||||
|
this.orientation,
|
||||||
});
|
});
|
||||||
|
|
||||||
ExifInfo copyWith({
|
ExifInfo copyWith({
|
||||||
@ -107,6 +113,7 @@ class ExifInfo {
|
|||||||
String? state,
|
String? state,
|
||||||
String? country,
|
String? country,
|
||||||
String? description,
|
String? description,
|
||||||
|
String? orientation,
|
||||||
}) =>
|
}) =>
|
||||||
ExifInfo(
|
ExifInfo(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@ -126,6 +133,7 @@ class ExifInfo {
|
|||||||
state: state ?? this.state,
|
state: state ?? this.state,
|
||||||
country: country ?? this.country,
|
country: country ?? this.country,
|
||||||
description: description ?? this.description,
|
description: description ?? this.description,
|
||||||
|
orientation: orientation ?? this.orientation,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -147,7 +155,8 @@ class ExifInfo {
|
|||||||
city == other.city &&
|
city == other.city &&
|
||||||
state == other.state &&
|
state == other.state &&
|
||||||
country == other.country &&
|
country == other.country &&
|
||||||
description == other.description;
|
description == other.description &&
|
||||||
|
orientation == other.orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -169,7 +178,8 @@ class ExifInfo {
|
|||||||
city.hashCode ^
|
city.hashCode ^
|
||||||
state.hashCode ^
|
state.hashCode ^
|
||||||
country.hashCode ^
|
country.hashCode ^
|
||||||
description.hashCode;
|
description.hashCode ^
|
||||||
|
orientation.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -192,10 +202,21 @@ class ExifInfo {
|
|||||||
state: $state,
|
state: $state,
|
||||||
country: $country,
|
country: $country,
|
||||||
description: $description,
|
description: $description,
|
||||||
|
orientation: $orientation
|
||||||
}""";
|
}""";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isOrientationFlipped(String? orientation) {
|
||||||
|
final value = orientation != null ? int.tryParse(orientation) : null;
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final isRotated90CW = value == 5 || value == 6 || value == 90;
|
||||||
|
final isRotated270CW = value == 7 || value == 8 || value == -90;
|
||||||
|
return isRotated90CW || isRotated270CW;
|
||||||
|
}
|
||||||
|
|
||||||
double? _exposureTimeToSeconds(String? s) {
|
double? _exposureTimeToSeconds(String? s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return null;
|
return null;
|
||||||
|
213
mobile/lib/entities/exif_info.entity.g.dart
generated
213
mobile/lib/entities/exif_info.entity.g.dart
generated
@ -87,13 +87,18 @@ const ExifInfoSchema = CollectionSchema(
|
|||||||
name: r'model',
|
name: r'model',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'state': PropertySchema(
|
r'orientation': PropertySchema(
|
||||||
id: 14,
|
id: 14,
|
||||||
|
name: r'orientation',
|
||||||
|
type: IsarType.string,
|
||||||
|
),
|
||||||
|
r'state': PropertySchema(
|
||||||
|
id: 15,
|
||||||
name: r'state',
|
name: r'state',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'timeZone': PropertySchema(
|
r'timeZone': PropertySchema(
|
||||||
id: 15,
|
id: 16,
|
||||||
name: r'timeZone',
|
name: r'timeZone',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
)
|
)
|
||||||
@ -154,6 +159,12 @@ int _exifInfoEstimateSize(
|
|||||||
bytesCount += 3 + value.length * 3;
|
bytesCount += 3 + value.length * 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
final value = object.orientation;
|
||||||
|
if (value != null) {
|
||||||
|
bytesCount += 3 + value.length * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
final value = object.state;
|
final value = object.state;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@ -189,8 +200,9 @@ void _exifInfoSerialize(
|
|||||||
writer.writeString(offsets[11], object.make);
|
writer.writeString(offsets[11], object.make);
|
||||||
writer.writeFloat(offsets[12], object.mm);
|
writer.writeFloat(offsets[12], object.mm);
|
||||||
writer.writeString(offsets[13], object.model);
|
writer.writeString(offsets[13], object.model);
|
||||||
writer.writeString(offsets[14], object.state);
|
writer.writeString(offsets[14], object.orientation);
|
||||||
writer.writeString(offsets[15], object.timeZone);
|
writer.writeString(offsets[15], object.state);
|
||||||
|
writer.writeString(offsets[16], object.timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExifInfo _exifInfoDeserialize(
|
ExifInfo _exifInfoDeserialize(
|
||||||
@ -215,8 +227,9 @@ ExifInfo _exifInfoDeserialize(
|
|||||||
make: reader.readStringOrNull(offsets[11]),
|
make: reader.readStringOrNull(offsets[11]),
|
||||||
mm: reader.readFloatOrNull(offsets[12]),
|
mm: reader.readFloatOrNull(offsets[12]),
|
||||||
model: reader.readStringOrNull(offsets[13]),
|
model: reader.readStringOrNull(offsets[13]),
|
||||||
state: reader.readStringOrNull(offsets[14]),
|
orientation: reader.readStringOrNull(offsets[14]),
|
||||||
timeZone: reader.readStringOrNull(offsets[15]),
|
state: reader.readStringOrNull(offsets[15]),
|
||||||
|
timeZone: reader.readStringOrNull(offsets[16]),
|
||||||
);
|
);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
@ -260,6 +273,8 @@ P _exifInfoDeserializeProp<P>(
|
|||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 15:
|
case 15:
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
|
case 16:
|
||||||
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
default:
|
default:
|
||||||
throw IsarError('Unknown property with id $propertyId');
|
throw IsarError('Unknown property with id $propertyId');
|
||||||
}
|
}
|
||||||
@ -1909,6 +1924,155 @@ extension ExifInfoQueryFilter
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationIsNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNull(
|
||||||
|
property: r'orientation',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition>
|
||||||
|
orientationIsNotNull() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||||
|
property: r'orientation',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationEqualTo(
|
||||||
|
String? value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'orientation',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition>
|
||||||
|
orientationGreaterThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
include: include,
|
||||||
|
property: r'orientation',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationLessThan(
|
||||||
|
String? value, {
|
||||||
|
bool include = false,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.lessThan(
|
||||||
|
include: include,
|
||||||
|
property: r'orientation',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationBetween(
|
||||||
|
String? lower,
|
||||||
|
String? upper, {
|
||||||
|
bool includeLower = true,
|
||||||
|
bool includeUpper = true,
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.between(
|
||||||
|
property: r'orientation',
|
||||||
|
lower: lower,
|
||||||
|
includeLower: includeLower,
|
||||||
|
upper: upper,
|
||||||
|
includeUpper: includeUpper,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationStartsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.startsWith(
|
||||||
|
property: r'orientation',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationEndsWith(
|
||||||
|
String value, {
|
||||||
|
bool caseSensitive = true,
|
||||||
|
}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.endsWith(
|
||||||
|
property: r'orientation',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationContains(
|
||||||
|
String value,
|
||||||
|
{bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.contains(
|
||||||
|
property: r'orientation',
|
||||||
|
value: value,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationMatches(
|
||||||
|
String pattern,
|
||||||
|
{bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.matches(
|
||||||
|
property: r'orientation',
|
||||||
|
wildcard: pattern,
|
||||||
|
caseSensitive: caseSensitive,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> orientationIsEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'orientation',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition>
|
||||||
|
orientationIsNotEmpty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||||
|
property: r'orientation',
|
||||||
|
value: '',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> stateIsNull() {
|
QueryBuilder<ExifInfo, ExifInfo, QAfterFilterCondition> stateIsNull() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addFilterCondition(const FilterCondition.isNull(
|
return query.addFilterCondition(const FilterCondition.isNull(
|
||||||
@ -2377,6 +2541,18 @@ extension ExifInfoQuerySortBy on QueryBuilder<ExifInfo, ExifInfo, QSortBy> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> sortByOrientation() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'orientation', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> sortByOrientationDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'orientation', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> sortByState() {
|
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> sortByState() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'state', Sort.asc);
|
return query.addSortBy(r'state', Sort.asc);
|
||||||
@ -2584,6 +2760,18 @@ extension ExifInfoQuerySortThenBy
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> thenByOrientation() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'orientation', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> thenByOrientationDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'orientation', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> thenByState() {
|
QueryBuilder<ExifInfo, ExifInfo, QAfterSortBy> thenByState() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'state', Sort.asc);
|
return query.addSortBy(r'state', Sort.asc);
|
||||||
@ -2701,6 +2889,13 @@ extension ExifInfoQueryWhereDistinct
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, ExifInfo, QDistinct> distinctByOrientation(
|
||||||
|
{bool caseSensitive = true}) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addDistinctBy(r'orientation', caseSensitive: caseSensitive);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<ExifInfo, ExifInfo, QDistinct> distinctByState(
|
QueryBuilder<ExifInfo, ExifInfo, QDistinct> distinctByState(
|
||||||
{bool caseSensitive = true}) {
|
{bool caseSensitive = true}) {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
@ -2809,6 +3004,12 @@ extension ExifInfoQueryProperty
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<ExifInfo, String?, QQueryOperations> orientationProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addPropertyName(r'orientation');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<ExifInfo, String?, QQueryOperations> stateProperty() {
|
QueryBuilder<ExifInfo, String?, QQueryOperations> stateProperty() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addPropertyName(r'state');
|
return query.addPropertyName(r'state');
|
||||||
|
@ -9,6 +9,7 @@ import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart
|
|||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_viewer/custom_video_player_controls.dart';
|
import 'package:immich_mobile/widgets/asset_viewer/custom_video_player_controls.dart';
|
||||||
import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart';
|
import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart';
|
||||||
import 'package:native_video_player/native_video_player.dart';
|
import 'package:native_video_player/native_video_player.dart';
|
||||||
@ -76,6 +77,16 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
type: VideoSourceType.file,
|
type: VideoSourceType.file,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
final assetWithExif =
|
||||||
|
await ref.read(assetServiceProvider).loadExif(asset);
|
||||||
|
final shouldFlip = assetWithExif.exifInfo?.isFlipped ?? false;
|
||||||
|
width.value = (shouldFlip ? assetWithExif.height : assetWithExif.width)
|
||||||
|
?.toDouble() ??
|
||||||
|
width.value;
|
||||||
|
height.value = (shouldFlip ? assetWithExif.width : assetWithExif.height)
|
||||||
|
?.toDouble() ??
|
||||||
|
height.value;
|
||||||
|
|
||||||
// Use a network URL for the video player controller
|
// Use a network URL for the video player controller
|
||||||
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||||
final String videoUrl = asset.livePhotoVideoId != null
|
final String videoUrl = asset.livePhotoVideoId != null
|
||||||
@ -93,10 +104,14 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
// When the volume changes, set the volume
|
// When the volume changes, set the volume
|
||||||
ref.listen(videoPlayerControlsProvider.select((value) => value.mute),
|
ref.listen(videoPlayerControlsProvider.select((value) => value.mute),
|
||||||
(_, mute) {
|
(_, mute) {
|
||||||
if (mute) {
|
try {
|
||||||
controller.value?.setVolume(0.0);
|
if (mute) {
|
||||||
} else {
|
controller.value?.setVolume(0.0);
|
||||||
controller.value?.setVolume(0.7);
|
} else {
|
||||||
|
controller.value?.setVolume(0.7);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -110,16 +125,24 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
// Find the position to seek to
|
// Find the position to seek to
|
||||||
final Duration seek = asset.duration * (position / 100.0);
|
final Duration seek = asset.duration * (position / 100.0);
|
||||||
controller.value?.seekTo(seek.inSeconds);
|
try {
|
||||||
|
controller.value?.seekTo(seek.inSeconds);
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// When the custom video controls paus or plays
|
// When the custom video controls paus or plays
|
||||||
ref.listen(videoPlayerControlsProvider.select((value) => value.pause),
|
ref.listen(videoPlayerControlsProvider.select((value) => value.pause),
|
||||||
(_, pause) {
|
(_, pause) {
|
||||||
if (pause) {
|
try {
|
||||||
controller.value?.pause();
|
if (pause) {
|
||||||
} else {
|
controller.value?.pause();
|
||||||
controller.value?.play();
|
} else {
|
||||||
|
controller.value?.play();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -153,8 +176,12 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onPlaybackReady() {
|
void onPlaybackReady() {
|
||||||
controller.value?.play();
|
try {
|
||||||
controller.value?.setVolume(0.9);
|
controller.value?.play();
|
||||||
|
controller.value?.setVolume(0.9);
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onPlaybackPositionChanged() {
|
void onPlaybackPositionChanged() {
|
||||||
@ -162,8 +189,12 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onPlaybackEnded() {
|
void onPlaybackEnded() {
|
||||||
if (loopVideo) {
|
try {
|
||||||
controller.value?.play();
|
if (loopVideo) {
|
||||||
|
controller.value?.play();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,12 +230,17 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
return () {
|
return () {
|
||||||
bufferingTimer.value.cancel();
|
bufferingTimer.value.cancel();
|
||||||
controller.value?.onPlaybackPositionChanged
|
try {
|
||||||
.removeListener(onPlaybackPositionChanged);
|
controller.value?.onPlaybackPositionChanged
|
||||||
controller.value?.onPlaybackStatusChanged
|
.removeListener(onPlaybackPositionChanged);
|
||||||
.removeListener(onPlaybackPositionChanged);
|
controller.value?.onPlaybackStatusChanged
|
||||||
controller.value?.onPlaybackReady.removeListener(onPlaybackReady);
|
.removeListener(onPlaybackPositionChanged);
|
||||||
controller.value?.onPlaybackEnded.removeListener(onPlaybackEnded);
|
controller.value?.onPlaybackReady.removeListener(onPlaybackReady);
|
||||||
|
controller.value?.onPlaybackEnded.removeListener(onPlaybackEnded);
|
||||||
|
controller.value?.stop();
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
|
@ -33,8 +33,14 @@ class VideoPlaybackValue {
|
|||||||
factory VideoPlaybackValue.fromNativeController(
|
factory VideoPlaybackValue.fromNativeController(
|
||||||
NativeVideoPlayerController controller,
|
NativeVideoPlayerController controller,
|
||||||
) {
|
) {
|
||||||
final playbackInfo = controller.playbackInfo;
|
PlaybackInfo? playbackInfo;
|
||||||
final videoInfo = controller.videoInfo;
|
VideoInfo? videoInfo;
|
||||||
|
try {
|
||||||
|
playbackInfo = controller.playbackInfo;
|
||||||
|
videoInfo = controller.videoInfo;
|
||||||
|
} catch (_) {
|
||||||
|
// Consume error from the controller
|
||||||
|
}
|
||||||
late VideoPlaybackState s;
|
late VideoPlaybackState s;
|
||||||
if (playbackInfo?.status == null) {
|
if (playbackInfo?.status == null) {
|
||||||
s = VideoPlaybackState.initializing;
|
s = VideoPlaybackState.initializing;
|
||||||
|
@ -4,7 +4,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/utils/db.dart';
|
import 'package:immich_mobile/utils/db.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
const int targetVersion = 6;
|
const int targetVersion = 7;
|
||||||
|
|
||||||
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
||||||
final int version = Store.get(StoreKey.version, 1);
|
final int version = Store.get(StoreKey.version, 1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user