Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image and video player improvements #1369

Merged
merged 7 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extensions:
62 changes: 62 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
PODS:
- background_fetch (1.3.2):
- Flutter
- Cache (6.0.0)
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
Expand All @@ -10,6 +14,13 @@ PODS:
- Flutter
- flutter_icmp_ping (0.0.1):
- Flutter
- flutter_inappwebview (0.0.1):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
- OrderedSet (~> 5.0)
- flutter_inappwebview/Core (0.0.1):
- Flutter
- OrderedSet (~> 5.0)
- flutter_keyboard_visibility (0.0.1):
- Flutter
- flutter_local_notifications (0.0.1):
Expand All @@ -21,19 +32,40 @@ PODS:
- gal (1.0.0):
- Flutter
- FlutterMacOS
- GCDWebServer (3.5.4):
- GCDWebServer/Core (= 3.5.4)
- GCDWebServer/Core (3.5.4)
- HLSCachingReverseProxyServer (0.1.0):
- GCDWebServer (~> 3.5)
- PINCache (>= 3.0.1-beta.3)
- image_picker_ios (0.0.1):
- Flutter
- OrderedSet (5.0.0)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- PINCache (3.0.4):
- PINCache/Arc-exception-safe (= 3.0.4)
- PINCache/Core (= 3.0.4)
- PINCache/Arc-exception-safe (3.0.4):
- PINCache/Core
- PINCache/Core (3.0.4):
- PINOperation (~> 1.2.3)
- PINOperation (1.2.3)
- pointer_interceptor_ios (0.0.1):
- Flutter
- push_ios (0.0.1):
- Flutter
- river_player (0.0.1):
- Cache (~> 6.0.0)
- Flutter
- GCDWebServer
- HLSCachingReverseProxyServer
- PINCache
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
Expand Down Expand Up @@ -61,16 +93,20 @@ PODS:
- Flutter
- url_launcher_ios (0.0.1):
- Flutter
- wakelock_plus (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter

DEPENDENCIES:
- background_fetch (from `.symlinks/plugins/background_fetch/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_custom_tabs_ios (from `.symlinks/plugins/flutter_custom_tabs_ios/ios`)
- flutter_file_dialog (from `.symlinks/plugins/flutter_file_dialog/ios`)
- flutter_icmp_ping (from `.symlinks/plugins/flutter_icmp_ping/ios`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
Expand All @@ -82,21 +118,31 @@ DEPENDENCIES:
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
- push_ios (from `.symlinks/plugins/push_ios/ios`)
- river_player (from `.symlinks/plugins/river_player/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)

SPEC REPOS:
trunk:
- Cache
- GCDWebServer
- HLSCachingReverseProxyServer
- OrderedSet
- PINCache
- PINOperation
- sqlite3

EXTERNAL SOURCES:
background_fetch:
:path: ".symlinks/plugins/background_fetch/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
Expand All @@ -107,6 +153,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_file_dialog/ios"
flutter_icmp_ping:
:path: ".symlinks/plugins/flutter_icmp_ping/ios"
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
flutter_keyboard_visibility:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
flutter_local_notifications:
Expand All @@ -129,6 +177,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/pointer_interceptor_ios/ios"
push_ios:
:path: ".symlinks/plugins/push_ios/ios"
river_player:
:path: ".symlinks/plugins/river_player/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
Expand All @@ -141,34 +191,46 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/uni_links/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"

SPEC CHECKSUMS:
background_fetch: 2319bf7e18237b4b269430b7f14d177c0df09c5a
Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_custom_tabs_ios: 62439c843b2691aae516fd50119a01eb9755fff7
flutter_file_dialog: 4c014a45b105709a27391e266c277d7e588e9299
flutter_icmp_ping: 2b159955eee0c487c766ad83fec224ae35e7c935
flutter_inappwebview: 3d32228f1304635e7c028b0d4252937730bbc6cf
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
flutter_sharing_intent: e35380d0e1501d7111dbb7e46d5ac6339da6da98
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181
image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
PINCache: d9a87a0ff397acffe9e2f0db972ac14680441158
PINOperation: fb563bcc9c32c26d6c78aaff967d405aa2ee74a7
pointer_interceptor_ios: 9280618c0b2eeb80081a343924aa8ad756c21375
push_ios: 2bd1b4d3f782209da1f571db1250af236957e807
river_player: ba880eae2d34deaff38fdf53a96b63edc654c9bf
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
sqlite3: 73b7fc691fdc43277614250e04d183740cb15078
sqlite3_flutter_libs: af0e8fe9bce48abddd1ffdbbf839db0302d72d80
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36

PODFILE CHECKSUM: 8d23d5c4d896af3a5f2a08e0206462ca9882e556
Expand Down
12 changes: 6 additions & 6 deletions lib/community/utils/post_card_action_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ final List<ExtendedPostCardActions> postCardActionItems = [
postCardAction: PostCardAction.shareMedia,
icon: Icons.image_rounded,
label: l10n.shareMedia,
getSubtitleLabel: (context, postViewMedia) => postViewMedia.media.first.mediaUrl,
getSubtitleLabel: (context, postViewMedia) => postViewMedia.media.first.thumbnailUrl,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.shareLink,
Expand Down Expand Up @@ -336,12 +336,12 @@ void showPostActionBottomModalSheet(
// Or if the media link is the same as the external link
if (postViewMedia.media.isEmpty ||
(postViewMedia.media.first.mediaType != MediaType.link && postViewMedia.media.first.mediaType != MediaType.image) ||
postViewMedia.media.first.originalUrl == postViewMedia.media.first.mediaUrl) {
postViewMedia.media.first.originalUrl == postViewMedia.media.first.thumbnailUrl) {
sharePostCardActions.removeWhere((extendedAction) => extendedAction.postCardAction == PostCardAction.shareLink);
}

// Remove the share media option if there is no media
if (postViewMedia.media.isEmpty || postViewMedia.media.first.mediaUrl == null) {
if (postViewMedia.media.isEmpty || postViewMedia.media.first.thumbnailUrl == null) {
sharePostCardActions.removeWhere((extendedAction) => extendedAction.postCardAction == PostCardAction.shareMedia);
}

Expand Down Expand Up @@ -582,18 +582,18 @@ class _PostCardActionPickerState extends State<PostCardActionPicker> {
break;
case PostCardAction.shareMedia:
action = () async {
if (widget.postViewMedia.media.first.mediaUrl != null) {
if (widget.postViewMedia.media.first.thumbnailUrl != null) {
try {
// Try to get the cached image first
var media = await DefaultCacheManager().getFileFromCache(widget.postViewMedia.media.first.mediaUrl!);
var media = await DefaultCacheManager().getFileFromCache(widget.postViewMedia.media.first.thumbnailUrl!);
File? mediaFile = media?.file;

if (media == null) {
// Tell user we're downloading the image
showSnackbar(AppLocalizations.of(widget.outerContext)!.downloadingMedia);

// Download
mediaFile = await DefaultCacheManager().getSingleFile(widget.postViewMedia.media.first.mediaUrl!);
mediaFile = await DefaultCacheManager().getSingleFile(widget.postViewMedia.media.first.thumbnailUrl!);
}

// Share
Expand Down
10 changes: 7 additions & 3 deletions lib/core/models/media.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ import 'package:thunder/core/enums/media_type.dart';
/// The Media class represents information for a given media source.
class Media {
Media({
this.thumbnailUrl,
this.mediaUrl,
this.originalUrl,
this.width,
this.height,
required this.mediaType,
});

/// The original URL of the media - this applies if the original URL of the media originates from a external link
/// The original external URL of the post
String? originalUrl;

/// The URL indicates the source of the media
/// The thumbnail URL of the media
String? thumbnailUrl;

/// The actual URL of the media source
String? mediaUrl;

/// The width of the media source
Expand All @@ -27,6 +31,6 @@ class Media {

@override
String toString() {
return '''Media { mediaUrl: $mediaUrl, originalUrl: $originalUrl, width: $width, height: $height, type: $mediaType }''';
return '''Media { thumbnailUrl: $thumbnailUrl, mediaUrl: $mediaUrl, originalUrl: $originalUrl, width: $width, height: $height, type: $mediaType }''';
}
}
4 changes: 4 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,10 @@
},
"failedToLoadBlocks": "Could not load blocks: {errorMessage}",
"@failedToLoadBlocks": {},
"failedToLoadVideo": "Failed to load video. Open link in browser?",
"@failedToLoadVideo": {
"description": "Error message that displays when we fail to load a video"
},
"failedToUnblock": "Could not unblock: {errorMessage}",
"@failedToUnblock": {},
"failedToUpdateNotificationSettings": "Failed to update notification settings",
Expand Down
28 changes: 18 additions & 10 deletions lib/post/utils/post.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,11 @@ Future<PostViewMedia> parsePostView(PostView postView, bool fetchImageDimensions
// There are three sources of URLs: the main url attached to the post, the thumbnail url attached to the post, and the video url attached to the post
String? url = postView.post.url ?? '';
String? thumbnailUrl = postView.post.thumbnailUrl;
String? videoUrl = postView.post.embedVideoUrl; // @TODO: Add support for videos
String? videoUrl = postView.post.embedVideoUrl;

// First, check what type of link we're dealing with based on the url (MediaType.image, MediaType.video, MediaType.link, MediaType.text)
bool isImage = isImageUrl(url);
bool isVideo = isVideoUrl(url);
bool isVideo = isVideoUrl(videoUrl ?? url);

MediaType mediaType;

Expand All @@ -296,29 +296,37 @@ Future<PostViewMedia> parsePostView(PostView postView, bool fetchImageDimensions

Media media = Media(mediaType: mediaType, originalUrl: url);

// Determine the thumbnail url
if (thumbnailUrl != null && thumbnailUrl.isNotEmpty) {
// Now check to see if there is a thumbnail image. If there is, we'll use that for the image
media.mediaUrl = thumbnailUrl;
media.thumbnailUrl = thumbnailUrl;
} else if (isImage) {
// If there is no thumbnail image, but the url is an image, we'll use that for the mediaUrl
media.mediaUrl = url;
// If there is no thumbnail image, but the url is an image, we'll use that for the thumbnailUrl
media.thumbnailUrl = url;
} else if (scrapeMissingPreviews) {
// If there is no thumbnail image, we'll see if we should try to fetch the link metadata
LinkInfo linkInfo = await getLinkInfo(url);

if (linkInfo.imageURL != null && linkInfo.imageURL!.isNotEmpty) {
media.mediaUrl = linkInfo.imageURL!;
media.thumbnailUrl = linkInfo.imageURL!;
}
}

// Finally, check to see if we need to fetch the image dimensions
if (fetchImageDimensions && media.mediaUrl != null) {
// Determine the media url
if (isImage) {
media.mediaUrl = url;
} else if (isVideo) {
media.mediaUrl = videoUrl;
}

// Finally, check to see if we need to fetch the image dimensions for the thumbnail url
if (fetchImageDimensions && media.thumbnailUrl != null) {
Size result = Size(MediaQuery.of(GlobalContext.context).size.width, 200);

try {
result = await retrieveImageDimensions(imageUrl: media.mediaUrl ?? media.originalUrl).timeout(const Duration(seconds: 2));
result = await retrieveImageDimensions(imageUrl: media.thumbnailUrl ?? media.mediaUrl).timeout(const Duration(seconds: 2));
} catch (e) {
debugPrint('${media.mediaUrl ?? media.originalUrl} - $e: Falling back to default image size');
debugPrint('${media.thumbnailUrl ?? media.originalUrl} - $e: Falling back to default image size');
}

Size size = MediaExtension.getScaledMediaSize(width: result.width, height: result.height, offset: edgeToEdgeImages ? 0 : 24, tabletMode: tabletMode);
Expand Down
8 changes: 4 additions & 4 deletions lib/shared/advanced_share_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class AdvancedShareSheetOptions {
);
}

bool _hasImage(PostViewMedia postViewMedia) => postViewMedia.media.isNotEmpty && postViewMedia.media.first.mediaUrl != null;
bool _hasImage(PostViewMedia postViewMedia) => postViewMedia.media.isNotEmpty && postViewMedia.media.first.thumbnailUrl != null;

bool _hasText(PostViewMedia postViewMedia) => postViewMedia.postView.post.body?.isNotEmpty == true;

Expand Down Expand Up @@ -106,7 +106,7 @@ Future<Uint8List> generateShareImage(BuildContext context, AdvancedShareSheetOpt
],
if (options.includeImage && _hasImage(postViewMedia))
Image.network(
postViewMedia.media.first.mediaUrl!,
postViewMedia.media.first.thumbnailUrl!,
),
if (options.includeText && postViewMedia.postView.post.body?.isNotEmpty == true) ...[
if (_hasImage(postViewMedia)) const SizedBox(height: 10),
Expand Down Expand Up @@ -193,7 +193,7 @@ void showAdvancedShareSheet(BuildContext context, PostViewMedia postViewMedia) a
),
if (!_isImageCustomized(options, postViewMedia) && options.includeImage && _hasImage(postViewMedia))
ImagePreview(
url: postViewMedia.media.first.mediaUrl.toString(),
url: postViewMedia.media.first.thumbnailUrl.toString(),
isExpandable: true,
isComment: true,
showFullHeightImages: true,
Expand Down Expand Up @@ -326,7 +326,7 @@ void showAdvancedShareSheet(BuildContext context, PostViewMedia postViewMedia) a
Share.shareXFiles([XFile.fromData(snapshot.data!, mimeType: 'image/jpeg')], text: text);
} else {
setState(() => isDownloading = true);
final File file = await DefaultCacheManager().getSingleFile(postViewMedia.media.first.mediaUrl!);
final File file = await DefaultCacheManager().getSingleFile(postViewMedia.media.first.thumbnailUrl!);
setState(() => isDownloading = false);
Share.shareXFiles([XFile(file.path)], text: text);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/link_preview_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class LinkPreviewCard extends StatelessWidget {
width: ViewMode.compact.height,
color: theme.cardColor.darken(5),
child: Icon(
hideNsfw ? null : Icons.play_arrow,
hideNsfw ? null : Icons.language,
color: theme.colorScheme.onSecondaryContainer.withOpacity(read == true ? 0.55 : 1.0),
),
),
Expand Down
Loading
Loading