Giter Site home page Giter Site logo

capacitor-community / media Goto Github PK

View Code? Open in Web Editor NEW
89.0 16.0 48.0 1.62 MB

Capacitor plugin for saving and retrieving photos and videos, and managing photo albums.

Home Page: https://capacitor.ionicframework.com/docs/

Ruby 0.27% Java 2.48% Objective-C 0.13% Swift 2.94% JavaScript 0.06% TypeScript 93.07% HTML 0.12% CSS 0.93%
capacitor plugin media capacitor-plugin ionic angular

media's Introduction


Capacitor Media

@capacitor-community/media

Capacitor plugin for saving and retrieving photos and videos, and managing photo albums.


Sponsors

Chatness AI

Maintainers

Maintainer GitHub Social
Nisala Kalupahana nkalupahana
Stewan Silva stewones @stewones

Installation

npm install @capacitor-community/media

This plugin is currently for Capacitor 6. Add an @5 at the end to install for Capacitor 5.

After installing, be sure to sync by running ionic cap sync.

Migrating to Capacitor 6

There are a few breaking changes to take note of:

  • saveGif no longer exists. Use savePhoto for images and GIFs.
  • Error text has been changed. If you were checking for specific error messages, you should now use error.code, which will be accessDenied, argumentError, downloadError, or filesystemError.

API

Unless otherwise noted, there should be full feature parity between iOS and Android. Web is not supported.

getMedias(...)

getMedias(options?: MediaFetchOptions | undefined) => Promise<MediaResponse>

Get filtered thumbnails from camera roll. iOS only.

Code Examples

Param Type
options MediaFetchOptions

Returns: Promise<MediaResponse>


getMediaByIdentifier(...)

getMediaByIdentifier(options?: { identifier: string; } | undefined) => Promise<MediaPath>

Get a filesystem path to a full-quality media asset by its identifier. iOS only. This is not included for Android because on Android, a media asset's identifier IS its path! You can simply use the Filesystem plugin to work with it. On iOS, you have to turn the identifier into a path using this function. After that, you can use the Filesystem plugin, same as Android.

Code Examples

Param Type
options { identifier: string; }

Returns: Promise<MediaPath>


getAlbums()

getAlbums() => Promise<MediaAlbumResponse>

Get list of albums.

Code Examples

Returns: Promise<MediaAlbumResponse>


savePhoto(...)

savePhoto(options?: MediaSaveOptions | undefined) => Promise<PhotoResponse>

Saves a still photo or GIF to the camera roll.

On Android and iOS, this supports web URLs, base64 encoded images (e.g. data:image/jpeg;base64,...), and local files. On Android, all image formats supported by the user's photo viewer are supported. On iOS, most common image formats are supported.

Code Examples

Param Type
options MediaSaveOptions

Returns: Promise<PhotoResponse>


saveVideo(...)

saveVideo(options?: MediaSaveOptions | undefined) => Promise<PhotoResponse>

Saves a video to the camera roll.

On Android and iOS, this supports web URLs, base64 encoded videos (e.g. data:image/mp4;base64,...), and local files. On Android, all video formats supported by the user's photo viewer are supported. On iOS, the supported formats are based on whatever iOS supports at the time.

Code Examples

Param Type
options MediaSaveOptions

Returns: Promise<PhotoResponse>


createAlbum(...)

createAlbum(options: MediaAlbumCreate) => Promise<void>

Creates an album.

Code Examples

Param Type
options MediaAlbumCreate

getAlbumsPath()

getAlbumsPath() => Promise<AlbumsPathResponse>

Gets the path where album folders and their corresponding photos are stored on the Android filesystem. This can be used to identify your album by more than just its name on Android, in case there are multiple albums with the same name, which is possible on Android. Just compare the albums path to the start of the album identifier when getting albums.

Only available on Android.

Code Examples: basic, when saving media

Returns: Promise<AlbumsPathResponse>


Interfaces

MediaResponse

Prop Type
medias MediaAsset[]

MediaAsset

Prop Type Description
identifier string Platform-specific identifier
data string Data for a photo asset as a base64 encoded string (JPEG only supported)
creationDate string ISO date string for creation date of asset
fullWidth number Full width of original asset
fullHeight number Full height of original asset
thumbnailWidth number Width of thumbnail preview
thumbnailHeight number Height of thumbnail preview
location MediaLocation Location metadata for the asset

MediaLocation

Prop Type Description
latitude number GPS latitude image was taken at
longitude number GPS longitude image was taken at
heading number Heading of user at time image was taken
altitude number Altitude of user at time image was taken
speed number Speed of user at time image was taken

MediaFetchOptions

Prop Type Description
quantity number The number of photos to fetch, sorted by last created date descending. To paginate, just request a higher quantity -- OS caching should make this relatively performant.
thumbnailWidth number The width of thumbnail to return
thumbnailHeight number The height of thumbnail to return
thumbnailQuality number The quality of thumbnail to return as JPEG (0-100)
types "photos" | "videos" | "all" Which types of assets to return thumbnails for.
albumIdentifier string Which album identifier to query in (get identifier with getAlbums())
sort "mediaType" | "mediaSubtypes" | "sourceType" | "pixelWidth" | "pixelHeight" | "creationDate" | "modificationDate" | "isFavorite" | "burstIdentifier" | MediaSort[] Sort order of returned assets by field and ascending/descending

MediaSort

Prop Type
key "mediaType" | "mediaSubtypes" | "sourceType" | "pixelWidth" | "pixelHeight" | "creationDate" | "modificationDate" | "isFavorite" | "burstIdentifier"
ascending boolean

MediaPath

Prop Type Description
path string Path to media asset
identifier string Identifier for media asset

MediaAlbumResponse

Prop Type
albums MediaAlbum[]

MediaAlbum

Prop Type
identifier string
name string
type MediaAlbumType

PhotoResponse

Prop Type Description
filePath string Available on Android only.

MediaSaveOptions

Prop Type Description
path string Web URL, base64 encoded URI, or local file path to save.
albumIdentifier string Album identifier from getAlbums(). Since 5.0, identifier is used on both Android and iOS. Identifier is required on Android but not on iOS. On iOS 14+, if the identifier is not specified and no permissions have been requested yet, add-only permissions will be requested instead of full permissions (assuming NSPhotoLibraryAddUsageDescription is in Info.plist).
fileName string File name to save the image as in the album. Do not include extension. Android only.

MediaAlbumCreate

Prop Type
name string

AlbumsPathResponse

Prop Type
path string

Enums

MediaAlbumType

Members Value Description
Smart 'smart' Album is a "smart" album (such as Favorites or Recently Added)
Shared 'shared' Album is a cloud-shared album
User 'user' Album is a user-created album

iOS

You'll need to add the following to your app's Info.plist file:

<dict>
  ...
  <key>NSPhotoLibraryUsageDescription</key>
  <string>Describe why you need access to user's photos (getting albums and media)</string>
  <key>NSPhotoLibraryAddUsageDescription</key>
  <string>Describe why you need to add photos to user's photo library</string>
  ...
</dict>

Android

You'll need to add the following to your app's AndroidManifest.xml file:

<manifest>
  ...
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
  ...
</manifest>

Note the READ_MEDIA permissions -- these are new in Android 13!

Demo

Go the the example/ folder to play with an example app that should show all functionality of this plugin.

Contributors ✨

Thanks goes to these wonderful people (emoji key):


stewones

💻 📖 🚧

Zachary Keeton

💻

Pierre Grimaud

📖

Talles Alves

🚧

Zyad Yasser

🚧

Manuel Rodríguez

💻 🚧

Michael

💻

Nisala Kalupahana

💻 📖 💡 🚧

Masahiko Sakakibara

🚧

ha6-6ru

💻

Stephan Fischer

💻

Matheus Davidson

💻 📖

This project follows the all-contributors specification. Contributions of any kind welcome!

media's People

Contributors

dragermrb avatar ha6-6ru avatar matheusdavidson avatar nkalupahana avatar pgrimaud avatar rdlabo avatar stephan-fischer avatar stewones avatar tallesventura avatar zakton5 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

media's Issues

Album already exists but doesn't show in getAlbums

Describe the bug
I check to see if the album exists and if not create it, then save a video to it. The album now exists in my phone's gallery and the video file is there. However, when I run this code again, the album does not show in getAlbums(), so then my code attempts to create the album, but it says albumAlready exists.

I've seen the other reports about this error, which say the issue was resolved in 5.2.1 ... I am using 5.2.1 but this issue is slightly different as it's not returning the album in getAlbums() despite the album existing with a file in it (not an empty album)

To Reproduce
Steps to reproduce the behavior:

  • Here is the code I'm running my Ionic app:
    async getAlbum(name: string): Promise<MediaAlbum> {
      console.log("Get album: ", name);
      let albums = null;
      let album = null;

      albums = await Media.getAlbums();
      console.log("Albums: ", albums);
      album = albums.albums.find((a) => a.name === name);

      if (!album) {
        const createAlbumResponse = await Media.createAlbum({ name: name });
        console.log("Create album: ", createAlbumResponse);
        albums = await Media.getAlbums();
        console.log("albums: ", albums);
        album = albums.albums.find((a) => a.name === name);
      }

      console.log("Found this album: ", album);

      return album;
    },

    async downloadRealTalkAndroid(track: boolean) {
    
      const album = await this.getAlbum(this.albumName);
      if (!album) {
        console.log("Unable to get album");
        return;
      }
      console.log("In download method we got this album...:", album);

      try {
        const options: MediaSaveOptions = {
          path: this.decodedFilePath,
          albumIdentifier: album.identifier,
        };
        console.log("Media save options: ", options);

        Media.saveVideo(options);
        this.hasSavedVideo = true;
        const toast = await toastController.create({
          message: this.t("realtalk.downloading"),
          duration: 2000,
        });
        toast.present();
      } catch (error) {
        const toast = await toastController.create({
          message: this.t("realtalk.downloadingError"),
          duration: 2000,
        });
        toast.present();
      }
    },

This code runs once, and executes fine. The album is created and the file is saved there. Then the very next time I run this code it errors saying the album already exists and can't be created (despite not showing in getAlbums)

Expected behavior
I would expect the newly created folder with a file in it to show up in getAlbums(). I do see these albums:
{name: 'Download', identifier: '/storage/emulated/0/Download'}
{name: 'Camera', identifier: '/storage/emulated/0/DCIM/Camera'}
{name: 'WhatsApp Images', identifier: '/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images'}
{name: 'Messages', identifier: '/storage/emulated/0/Pictures/Messages'}
{name: 'Facebook', identifier: '/storage/emulated/0/DCIM/Facebook'}
{name: 'Snapchat', identifier: '/storage/emulated/0/DCIM/Snapchat'}
{name: 'Pictures', identifier: '/storage/emulated/0/Pictures'}
{name: 'DCIM', identifier: '/storage/emulated/0/DCIM'}
{name: 'PokemonGO', identifier: '/storage/emulated/0/Pictures/PokemonGO'}
{name: 'Screenshots', identifier: '/storage/emulated/0/Pictures/Screenshots'}
{name: 'Restored', identifier: '/storage/emulated/0/DCIM/Restored'}
{name: 'Reddit', identifier: '/storage/emulated/0/Pictures/Reddit'}
{name: 'PhotosEditor', identifier: '/storage/emulated/0/DCIM/PhotosEditor'}
{name: 'Lensa', identifier: '/storage/emulated/0/Pictures/Lensa'}
{name: 'Canva', identifier: '/storage/emulated/0/Pictures/Canva'}
{name: 'Documents', identifier: '/storage/emulated/0/Documents'}
{name: 'Giphy', identifier: '/storage/emulated/0/Pictures/Giphy'}
{name: 'Instagram', identifier: '/storage/emulated/0/Pictures/Instagram'}

However the recently created one is not showing, this was the response from createAlbum:
{name: 'X', identifier: '/storage/emulated/0/Android/media/com.x.x/X'}

Smartphone (please complete the following information):

  • Device: Google Pixel 7 Pro
  • OS: Android 13
  • Version: 5.2.1

Capacitor 5 Migration

Quite a few breaking changes I'd like to roll out for this:

  • Plugin and Example Migration
  • Return album identifiers on Android instead of just names (solves unreported duplicate album name issue, and just general API confusion)
  • Remove Pictures/Movies designation from code -- it doesn't even work right now b/c of incorrect string comparison
  • Ensure empty albums are reported by getAlbums ( solves #6 )

Should be done this month!

Record Audio

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

The proposal had capacitor-community/proposals#19
the support of recording audio. I need this and couldn't find any further community plugin.

Describe the solution you'd like
A clear and concise description of what you want to happen.

It would be similar to cordova-plugin-media. https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-media/
startRecord and stopRecord function

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Im unsure where this belongs maybe in native-audio. But I think it should be kept separate from the player.

Additional context
Add any other context or screenshots about the feature request here.

savePhoto fails with RuntimeException occurred in v4.0.1

Describe the bug
Camera Opens, Photo successful, then error on savePhoto()

To Reproduce

function scanDocument2() {
  var image = document.getElementById("myImage").src;
return Capacitor.Plugins.DocumentScanner.scanDocument({
responseType: 'imageFilePath'
}).then(function(result) {
const scannedImages = result.scannedImages;
if (scannedImages.length > 0) {
const scannedImage = document.getElementById('myImage');
if (scannedImage) {
scannedImage.src = Capacitor.convertFileSrc(scannedImages[0]);
console.log(scannedImage.src)
// scannedImage.src = JSON.stringify("data:image/jpeg;base64," + scannedImages[0]);

localStorage.setItem("imageSrc", scannedImage.src);
setTimeout(function() {


 Capacitor.Plugins.Media
        .savePhoto({
          path: scannedImage.src,
          album: "Pictures",
        })
        .then((r) => console.log(r))
        .catch((e) => console.error(e));
     

}, 2500);
}
}
});
}

Expected behavior
Photo should be saved to device

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: MacOS
  • Browser Chrome

Smartphone (please complete the following information):

  • Device: Android Simulator
  • Version API 33

Stacktrace:

savePhotoV/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 55940831, pluginId: Media, methodName: savePhoto
V/Capacitor: callback: 55940831, pluginId: Media, methodName: savePhoto, methodData: {"path":"http://localhost/capacitor_file/storage/emulated/0/Android/data/com.vetcalculators.sofie/files/Pictures/DOCUMENT_SCAN_0_20230206_0908269068608694537019606.jpg","album":"Pictures"}
D/DEBUG LOG: SAVE PHOTO TO ALBUM
D/DEBUG LOG: HAS PERMISSION
D/DEBUG LOG: ___SAVE MEDIA TO ALBUM
D/SDK BUILD VERSION: 33
D/ENV LOG - ALBUM DIR: /storage/emulated/0/Android/media/com.vetcalculators.sofie/Pictures
E/Capacitor/Plugin: RuntimeException occurred
java.lang.RuntimeException: Source file not found: /capacitor_file/storage/emulated/0/Android/data/com.vetcalculators.sofie/files/Pictures/DOCUMENT_SCAN_0_20230206_0908269068608694537019606.jpg, error: /capacitor_file/storage/emulated/0/Android/data/com.vetcalculators.sofie/files/Pictures/DOCUMENT_SCAN_0_20230206_0908269068608694537019606.jpg: open failed: ENOENT (No such file or directory)
at com.getcapacitor.community.media.MediaPlugin.copyFile(MediaPlugin.java:317)
at com.getcapacitor.community.media.MediaPlugin._saveMedia(MediaPlugin.java:255)
at com.getcapacitor.community.media.MediaPlugin.savePhoto(MediaPlugin.java:79)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:138)
at com.getcapacitor.Bridge.lambda$callPluginMethod$0$com-getcapacitor-Bridge(Bridge.java:763)
at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(Unknown Source:8)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.os.HandlerThread.run(HandlerThread.java:67)
D/Capacitor: Sending plugin error: {"save":false,"callbackId":"55940831","pluginId":"Media","methodName":"savePhoto","success":false,"error":{"message":"RuntimeException occurred"}}
E/Capacitor/Console: File: - Line 24 - Msg: Error: RuntimeException occurred
V/FA: Inactivity, disconnecting from the service

READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE are still required in Android 13

On Android 13, READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE are still required in AndroidManifest.xml. By right, it is not needed. Google might stringent their policy by rejecting app target SDK 33 with READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE without setting maxsdkversion.

This happen when calling api getAlbums to get identifier.

java.lang.RuntimeException: Source file not found when saving photo

Actually, the iOS version is fine. However, the Android got problem. It seems to me I have tested the code few months ago and it worked fine. But suddenly it doesn't work when I trying my device. Any idea?

V/Capacitor: callback: 125287492, pluginId: MediaPlugin, methodName: savePhoto, methodData: {"path":"https://##MYDOMAIN###/uploads/albums/462/IMG_9720.JPG","album":"Pictures"}
D/DEBUG LOG: SAVE VIDEO TO ALBUM
HAS PERMISSIONS
___SAVE MEDIA TO ALBUM
E/Capacitor/Plugin: RuntimeException occurred
java.lang.RuntimeException: Source file not found: /uploads/albums/462/IMG_9720.JPG, error: /uploads/albums/462/IMG_9720.JPG: open failed: ENOENT (No such file or directory)
at io.stewan.capacitor.media.MediaPlugin.copyFile(MediaPlugin.java:225)
at io.stewan.capacitor.media.MediaPlugin._saveMedia(MediaPlugin.java:189)
at io.stewan.capacitor.media.MediaPlugin.savePhoto(MediaPlugin.java:128)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$2.run(Bridge.java:537)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.os.HandlerThread.run(HandlerThread.java:67)
D/Capacitor: Sending plugin error: {"save":false,"callbackId":"125287492","pluginId":"MediaPlugin","methodName":"savePhoto","success":false,"error":{"message":"RuntimeException occurred"}}

Media.savePhoto's Promise<PhotoResponse> returns 'undefined' on iOS

Describe the bug
Version ^4.2.0 of Media plugin (have not yet moved to Capacitor 5)
Media.savePhoto's Promise returns 'undefined' on iOS but works fine on Android.

Code snippet:

  const photo = await Camera.getPhoto({
      ...options,
      ...customCameraOptions,
  });
  
  // Camera's photo.path is passed as 'path' to Media.savePhoto

 return await Media.savePhoto({
            path,
            album: this.platform.is('android')
                ? albumName // On Android targetAlbum will be undefined right after the creation
                : targetAlbum.identifier,
        }).then((photo) => { 
            console.log(`photos.service::savePhoto`, photo); 
            return photo 
        });

To Reproduce
Steps to reproduce the behavior:

  1. Use device's native camera to take a photo
  2. Pass the Camera.getPhoto... photo.path to Media.savePhoto...
  3. PhotoResponse is undefined in Promise's .then((photo)

Expected behavior
Expected iOS to work the same as Android, PhotoResponse should be defined and provide the filePath

Screenshots
NA

Desktop (please complete the following information):
NA

Smartphone (please complete the following information):

  • Device: iPhone 14 pro
  • OS: 16.5
  • Browser: Safari
  • Version ??

Additional context
NA

(android) Support for API 33 / Android 13 SDK target

Describe the bug
The Android 13/API 33 SDK makes breaking changes to the permissions required to read/write media.

READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE permissions are no longer supported and will not be granted even if included in AndroidManifest.xml and requested at run-time.

The permissions READ_MEDIA_IMAGES/READ_MEDIA_VIDEO/READ_MEDIA_AUDIO must instead be requested.

Currently if an app containing this plugin is built using API 33 as a target and is run on a device with Android 13+, plugin methods will return an error because the plugin is checking if EXTERNAL_STORAGE permissions have been granted.

To resolve this, the plugin will need to check the target API version and if >= 33, then check/request the READ_MEDIA permissions instead.

As a workaround, I have hacked isStoragePermissionGranted() to always return true and used Cordova Diagnostic plugin to check/request the appropriate READ_MEDIA permissions before invoking this plugin

To Reproduce
Steps to reproduce the behavior:

  1. Create a new app with Capacitor v5 which uses API 33 / Android 13 as target SDK
  2. Add this plugin
  3. Call a method from this plugin such as Media.getAlbums()
  4. Run on Android device emulator running Android 13+
  5. Observe reported error: Unable to do file operation, user denied permission request

Expected behavior
Works correctly with API 33 as target SDK

Saving media file to IOS photo library

Is your feature request related to a problem? Please describe.
Hi,
First of all, it would be great if you'll add a section of "forum" besides of feature request and bug report.
My problem should be in a discussion because it's neither feature request or bug report (I think)

I use the saveVideo method, I send url (tried base64 either) and i get error when reaching the part of the swift code of saving the downloaded media file to the photo library, the !success in the completionhandler is false.
you can see the following screenshot:

saveVideoPhotoLibrary

How do you handle this issue?

Thanks

App crash after downloading an image

Describe the bug
The app successfully download the image but then it crashes and close the app.
I have recently upgraded to Capacitor 5 following the guide and installed the latest version of this plugin.
The bug happens both in emulator and real device.

To Reproduce
I've attached screenshots of my code. During and after the crash there were no errors detected by both Chrome's device inspector and Android Studio.

Package.json:
"dependencies": { "@capacitor-community/file-opener": "1.0.5", "@capacitor-community/media": "^5.2.2", "@capacitor-community/photoviewer": "^3.0.1", "@capacitor/android": "^5.0.0", "@capacitor/app": "^5.0.0", "@capacitor/browser": "^5.0.0", "@capacitor/clipboard": "^5.0.0", "@capacitor/core": "^5.0.0", "@capacitor/device": "^5.0.0", "@capacitor/filesystem": "^5.0.0", "@capacitor/ios": "^5.0.0", "@capacitor/share": "^5.0.0", "@capacitor/splash-screen": "^5.0.0", "@ionic-native/background-mode": "^5.31.1", "@ionic-native/core": "5.30.0", "@ionic-native/document-viewer": "5.30.0", "@ionic-native/file": "5.30.0", "@ionic-native/file-opener": "5.30.0", "@ionic-native/onesignal": "5.30.0", "@ionic/vue": "5.5.2", "@ionic/vue-router": "5.5.2", "axios": "0.21.1", "cordova-plugin-background-mode": "^0.7.3", "cordova-plugin-customurlscheme-ng": "^11.0.0", "cordova-plugin-device": "^2.0.3", "cordova-plugin-doze-optimize": "^0.5.4", "cordova-support-android-plugin": "^1.0.2", "core-js": "3.8.2", "jetifier": "^1.6.6", "onesignal-cordova-plugin": "^3.2.0", "register-service-worker": "1.7.2", "vue": "3.0.5", "vue-router": "4.0.3", "vuex": "4.0.0" },

Attachments

  • Screen recording of the bug
  • Screenshot of the JS file using the plugin
  • Screenshot of the method using the function of the JS file

Additional context
A similar (if not the same) bug happens when using the Photoviewer plugin

Media.-.Crash.mp4
Screenshot 2023-09-15 alle 12 13 31 Screenshot 2023-09-15 alle 12 10 56

Album identifier does not exist, use get Albums() to get

I use getAlbums to find a album named "话树", unfortunately, when i savePhoto it says no exist?

Error: Album identifier does not exist, use getAlbums() to get
    at new CapacitorException (index.js?5d3a:85)
    at returnResult ((index):773)
    at Object.cap.fromNative ((index):755)
    at <anonymous>:1:18

albums
(12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {name: "Screenshots", identifier: "/storage/emulated/0/DCIM/Screenshots"}
1: {name: "想法", identifier: "/storage/emulated/0/Pictures/想法"}
2: {name: "百度图像搜索", identifier: "/storage/emulated/0/Pictures/graphSearch/百度图像搜索"}
3: {name: "Camera", identifier: "/storage/emulated/0/DCIM/Camera"}
4: {name: "WeiXin", identifier: "/storage/emulated/0/Pictures/WeiXin"}
5: {name: "downloads", identifier: "/storage/emulated/0/baidu/searchbox/downloads"}
6: {name: "QQ_Images", identifier: "/storage/emulated/0/Tencent/QQ_Images"}
7: {name: "QQ", identifier: "/storage/emulated/0/Pictures/QQ"}
8: {name: "Documents", identifier: "/storage/emulated/0/Documents"}
9: {name: "话树", identifier: "/storage/emulated/0/Android/media/com.chatree.chat/话树"}
10: {name: "sssss", identifier: "/storage/emulated/0/Android/media/com.chatree.chat/sssss"}
11: {name: "DCIM", identifier: "/storage/emulated/0/Android/media/com.chatree.chat/DCIM"}

Error: RuntimeException occurred : In SaveVideo method

Describe the bug
Sometimes getting Error: RuntimeException occurred while downloading video to specific location in android.

To Reproduce
Steps to reproduce the behavior:
Use saveVideo method with proper opts parameters
sometimes getting this error but video downloaded in downloads
If not getting error video downloaded in specific directory given

Video downloaded in both cases but why randomly happening?

image

Seems to be the copyfile function sometimes working and sometimes not working.

Plugin breaks on @capacitor/core@^3.0.0

Describe the bug
Because the plugin pulls @capacitor/core@latest, it is now breaking on 3.0.0 because of this:

@deprecated
Plugins should be imported instead. Deprecated in v3 and Capacitor.Plugins property definition will not be exported in v4.

Problems are reported on ionic serve:

[ng] Error: node_modules/@capacitor-community/media/node_modules/@capacitor/core/types/definitions-internal.d.ts:16:18 - error TS2430: Interface 'CapacitorInstance' incorrectly extends interface 'CapacitorGlobal'.
[ng]   Types of property 'Plugins' are incompatible.
[ng]     Property 'MediaPlugin' is missing in type '{ [pluginName: string]: { [prop: string]: any; }; }' but required in type 'PluginRegistry'.
[ng] 16 export interface CapacitorInstance extends CapacitorGlobal {
[ng]                     ~~~~~~~~~~~~~~~~~
[ng]   node_modules/@capacitor-community/media/dist/esm/definitions.d.ts:3:9
[ng]     3         MediaPlugin: MediaPluginProtocol;
[ng]               ~~~~~~~~~~~
[ng]     'MediaPlugin' is declared here.
[ng] Error: node_modules/@capacitor/filesystem/dist/esm/definitions.d.ts:1:15 - error TS2724: Module '"../../../core/dist/esm"' has no exported member 'PermissionState'. Did you mean 'PermissionType'?
[ng] 1 import type { PermissionState } from '@capacitor/core';
[ng]                 ~~~~~~~~~~~~~~~
[ng]   node_modules/@capacitor/core/dist/esm/core-plugin-definitions.d.ts:1243:21
[ng]     1243 export declare enum PermissionType {
[ng]                              ~~~~~~~~~~~~~~
[ng]     'PermissionType' is declared here.
[ng] Error: ./node_modules/@capacitor/filesystem/dist/esm/index.js 2:19-33
[ng] "export 'registerPlugin' was not found in '@capacitor/core'

To Reproduce
Steps to reproduce the behavior:
npm install @capacitor-community/media

Expected behavior
Working.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):
Any

Smartphone (please complete the following information):
any

Additional context

Unable to save Video - iOS 13.6 - Simulator

Hi There--

Describe the bug
When using the saveVideo function, I am receiving the below error.

"message":"Unable to save video to album","errorMessage":"The operation couldn’t be completed. (PHPhotosErrorDomain error -1.)"

I have given the app Read & Write permissions when prompted, and confirmed in the Settings.

I am following all steps in the ReadMe, and have included code and log below. Please advise. Thanks!

To Reproduce

Code:

console.log("album found!");
console.log("album: " + JSON.stringify(this.album));
console.log("platform : " + platform);
console.log("photo album info : " + JSON.stringify(this.album));
var albumOption = (platform == "ios" ? this.album.identifier : this.album.name);
var options = { path: this.video.url, album: albumOption };
console.log("saveVideo options : " + JSON.stringify(options));

media
.saveVideo(options)
.then(console.log)
.catch(console.log);

Log:

[log] - album found!
⚡️ [log] - album: {"name”:”MyAlbum”,”identifier":"A0B220CF-57D6-40F1-B2F7-CC49D88DDA95/L0/040","type":"user"}
⚡️ [log] - platform : ios
⚡️ [log] - photo album info : {"name”:”MyAlbum”,”identifier":"A0B220CF-57D6-40F1-B2F7-CC49D88DDA95/L0/040","type":"user"}
⚡️ [log] - saveVideo options : {"path":"https://s3-us-west-2.amazonaws.com/contentjaguar-hosted.com/image-hosted/11sustainablevid.mp4","album":"A0B220CF-57D6-40F1-B2F7-CC49D88DDA95/L0/040"}
⚡️ To Native -> MediaPlugin saveVideo 117408218
ERROR MESSAGE: {"message":"Unable to save video to album","errorMessage":"The operation couldn’t be completed. (PHPhotosErrorDomain error -1.)"}
⚡️ [log] - {"message":"Unable to save video to album","errorMessage":"The operation couldn’t be completed. (PHPhotosErrorDomain error -1.)"}

Expected behavior
Expected behavior is the video saves to the queried and found album.

Smartphone (please complete the following information):

  • Device: iPhone 8 Simulator w/ XCode
  • OS: iOS 13.6

Several crashes on Android reported by Crashlytics

Describe the bug
I'm getting many crashes reported by Firebase Crashlytics caused by some messaging dependency (com.google.firebase.iid.zzbe and com.getcapacitor.CapacitorFirebaseMessagingService) .

To Reproduce
I was not able to reproduce it consistently. The error happened more frequent in production.

Expected behavior
While I can handle some crashes here and there due to huge ecosystem Android has, I've had a very small number Crash-Free users on Android, 64%.

Screenshots
Link to gallery
SS 1
SS 2
SS 3
SS 4

Desktop (please complete the following information):
N/A

Smartphone (please complete the following information):
Android devices. No pattern has been found in terms of OS Versions

Additional context
I'm also using some other capacitor-community plugins that may have some impact (@capacitor-community/admob, @capacitor-community/firebase-analytics, @capacitor-community/firebase-crashlytics, @capacitor-community/firebase-remote-config, @capacitor-community/media). I'll attach the complete plugin list below:

ionic info
Ionic:

   Ionic CLI                     : 6.12.0 
   Ionic Framework               : @ionic/angular 5.3.4
   @angular-devkit/build-angular : 0.901.12
   @angular-devkit/schematics    : 9.1.12
   @angular/cli                  : 9.1.12
   @ionic/angular-toolkit        : 2.3.3

Capacitor:

   Capacitor CLI   : 2.4.2
   @capacitor/core : 2.4.2

Utility:

   cordova-res                          : not installed
   native-run (update available: 1.2.1) : 1.0.0

System:

   NodeJS : v14.6.0
   npm    : 6.14.8
   OS     : macOS Catalina

package.json

"dependencies": {
    "@angular/common": "^9.1.12",
    "@angular/core": "^9.1.12",
    "@angular/forms": "^9.1.12",
    "@angular/platform-browser": "^9.1.12",
    "@angular/platform-browser-dynamic": "^9.1.12",
    "@angular/router": "^9.1.12",
    "@capacitor-community/admob": "^1.2.0-0",
    "@capacitor-community/firebase-analytics": "^0.2.0",
    "@capacitor-community/firebase-crashlytics": "^0.3.0",
    "@capacitor-community/firebase-remote-config": "^0.1.3",
    "@capacitor-community/media": "^1.0.1",
    "@capacitor/android": "^2.4.0",
    "@capacitor/core": "^2.4.0",
    "@capacitor/ios": "^2.4.0",
    "@ionic-native/core": "^5.28.0",
    "@ionic-super-tabs/angular": "^7.0.8",
    "@ionic/angular": "^5.3.2",
    "@ionic/storage": "^2.3.0",
    "@ngx-translate/core": "^12.1.2",
    "@ngx-translate/http-loader": "^4.0.0",
    "cordova-sqlite-storage": "^5.0.1",
    "ngx-countup": "^7.3.3",
    "rxjs": "~6.5.1",
    "tslib": "^1.10.0",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.901.12",
    "@angular/cli": "^9.1.12",
    "@angular/compiler": "^9.1.12",
    "@angular/compiler-cli": "^9.1.12",
    "@angular/language-service": "^9.1.12",
    "@capacitor/cli": "^2.4.0",
    "@ionic/angular-toolkit": "^2.3.3",
    "@types/jasmine": "^3.5.14",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.12.54",
    "codelyzer": "^5.1.2",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "^5.2.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~3.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "ts-node": "~8.3.0",
    "tslint": "^6.1.3",
    "typescript": "~3.8.3"
  }

Error while copying media file

File: MediaPlugin
Line: 299
Code fragment:

try {
    // generate image file name using current date and time
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmssSSS").format(new Date());
    String fileName = call.getString("fileName", "IMG_" + timeStamp);
    File expFile = copyFile(inputFile, albumDir, fileName);
    scanPhoto(expFile);

    JSObject result = new JSObject();
    result.put("filePath", expFile.toString());
    call.resolve(result);
} catch (RuntimeException e) {
    call.reject(e.getLocalizedMessage(), null, e); // I changed this line to get the error message in javascript layer.
}

Explanation.
Some of Android users can't download media files regardress of OS versions.
I couldn't reproduce this error on my devices.
Error logs starting with "RuntimeException occurred ..." didn't help at all, so to check the detailed error message I changed the catch clause in the plugin file and attached the new logs reported on my server below.

# case 1 
[Error]
Copy file not found: /storage/emulated/0/iPhoneData/Pictures/학교종이/IMG_20230830_152558883.jpg, error: /storage/emulated/0/iPhoneData/Pictures/학교종이/IMG_20230830_152558883.jpg: open failed: EPERM (Operation not permitted)

[Parms] 
path: file:///storage/emulated/0/Android/data/com.schoolbell_e.schoolbell_e/files/intermediate.jpg
albumIdentifier: 학교종이

[Device info]
"os_version": "13",
"app_version": "5.0.51",
"device": "SM-F731N",


# case 2 
[Error]
Copy file not found: /storage/emulated/0/학교종이/IMG_20230830_144007181.jpg, error: /storage/emulated/0/학교종이/IMG_20230830_144007181.jpg: open failed: EPERM (Operation not permitted)

[Parms] 
path: file:///storage/emulated/0/Android/data/com.schoolbell_e.schoolbell_e/files/cdv_photo_000_high.jpg
albumIdentifier: 학교종이

[Device info]
"platform": "android",
"os_version": "13",

"device": "SM-S908N",


# case 3
[Error]
Copy file not found: /storage/emulated/0/학교종이/IMG_20230830_125834428.jpg, error: /storage/emulated/0/학교종이/IMG_20230830_125834428.jpg: open failed: EPERM (Operation not permitted)

[Parms] 
path: file:///storage/emulated/0/Android/data/com.schoolbell_e.schoolbell_e/files/%EA%B5%90%EC%9C%A1%EA%B3%B5%EB%8F%99%EC%B2%B4%EC%9D%98%20%EB%82%A0%20%EC%95%88%EB%82%B4%EB%AC%B8001.jpg
albumIdentifier: 학교종이

[Device info]
"platform": "android",
"os_version": "13",
"device": "SM-G996N",

getMedias for android not implemented

Describe the solution you'd like
When do you plan to implement getMedias for Android? I'm building an app which needs to access all device photos, from
Recent album, and this feature is critical :)

Additional context
Meanwhile, could someone recommend a capacitor or cordova plugin which allowe access to all device photos? Thanks

Cannot read property 'createAlbum' of undefined

Platform Android:

import { Media } from '@capacitor-community/media';

async function createAlbumForSave(imageURI){

      const media = new Media(); 
    const platform = Capacitor.getPlatform(); 
    const albumName = 'PoliticalFrames';
    let albumIdentifier = '';

        if (platform === 'ios') {
          // Handle albums
          let albums = await media.getAlbums();
          albumIdentifier = albums.albums.find(a => a.name === albumName)?.identifier || null;
          
          if (!albumIdentifier) {
            // Doesn't exist, create new album
            await media.createAlbum({ name: albumName });
            albums = await media.getAlbums();
            albumIdentifier = albums.albums.find(a => a.name === albumName)?.identifier;
          }     } else if (platform === 'android') {
            await media.createAlbum({ name: albumName });
         }

         media.savePhoto({
          path: imageURI,
          album: (platform === 'ios') ? albumIdentifier : albumName
        })
        .then(() => console.log('Image has been saved'))
        .catch(e => console.error(e));

    }

ERROR : Cannot read property 'createAlbum' of undefined

Can't add photos to new album on iOS

THE PROBLEM:
Creating a new album on iOS always creates a "smart" album, so then when I try to add a new photo to the album, I get this:

Error: Album doesn't support adding content (is this a smart album?)

REPRODUCE:

  1. Create a new album with createAlbum()
  2. Call getAlbums() and find the album identifier of the album you just created
  3. Try to add a new photo with savePhoto() and pass the identifier of the new album

EXPECTED BEHAVIOR
I would expect that new albums I create would be of type "user", not of type "smart". I'm assuming that if it's of type "user" that I would then be able to add photos to it. What's the point of creating a new album if I can't add photos to it with savePhoto()?

DEVICE & SOFTWARE

  • Device: iPad 8th Gen
  • OS: iPadOS 16.6
  • Xcode: 14.3.1

Am I missing something here?

I need a feature in which we can get all media files from specific folder from ios like camera etc

Is your feature request related to a problem? Please describe.
I have an app in which i need to get all local albums from ios photos which i am able to show using this plugin but another thing which i want is to get all the media file from any spacific albums that i previously get from media plugin.

Describe the solution you'd like
A method / function in which i ll add album name and able to get all files in that folder/album

Describe alternatives you've considered
file plugin for android was able to do same thing but on ios i think it has some issue .. thats why i look for that plugin

Additional context

I also look for some apps on ios that do that so i think its possible to do ...

Please explain if u can make that or is there any option already available in your plugin

Thanks

Fix documentation

You might want to fix the documentation.

The "savePhotos|Videos|Gif()" function now takes an object as the album parameter, not a string anymore.

before

Media.savePhoto({
    path: "<path>",
    album: "<albumId/name>"
})

now

Media.savePhoto({
    path: "<path>",
    album: {
        id: "<albumId>",
        name: "<albumName>"
    }
})

option to sort getMedias

Is your feature request related to a problem? Please describe.
Current, getMedias method brings media sorted by first to last.

Describe the solution you'd like
I would like to be able to change the order of the pictures got by getMedias , so it shows latest pictures instead.

Describe alternatives you've considered
Noticed on apple documentation that there is an option for sorting on PHFetchOptions. The implementation would be like this:

let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "startDate", ascending: false)]

Additional context
I'll send a PR to implement that soon.

App is crashed on Android due to EACCES (Permission denied)

image

Caused by: java.lang.RuntimeException: Source file not found: /storage/emulated/0/Download/fd7df7f4-12ba-4a76-b687-27088727e873-1.gif, error: /storage/emulated/0/Download/fd7df7f4-12ba-4a76-b687-27088727e873-1.gif: open failed: EACCES (Permission denied)

It's so interesting issue, because It is not reproducible all the time.

AndroidManifest:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

Fragment code:

private async getIdentifierAlbum() {
    const { albums } = await Media.getAlbums();

    const album = albums.find((a) => a.name === ALBUM_NAME);

    return album ? album.identifier : null;
  }

  private async downloadMediaFile(path: string, albumIdentifier: string, mediaType: MediaType) {
    let response: Maybe<Promise<PhotoResponse>> = null;
    let successMessage = '';
    let errroMessage = '';

    if (mediaType === 'image') {
      response = Media.savePhoto({
        path,
        albumIdentifier,
      });
      successMessage = 'You saved the photo';
      errroMessage = 'Failed to save the photo';
    }

    if (mediaType === 'video') {
      response = Media.saveVideo({
        path,
        albumIdentifier,
      });
      successMessage = 'You saved the video';
      errroMessage = 'Failed to save the video';
    }

    if (mediaType === 'gif') {
      response = Media.saveGif({
        path,
        albumIdentifier,
      });
      successMessage = 'You saved the gif';
      errroMessage = 'Failed to save the gif';
    }

    if (response) {
      try {
        await response;

        this.setSuccesses([
          {
            message: successMessage,
            delay: DOWNLOAD_NOTIFICATION_DELAY,
          },
        ]);
      } catch (error) {
        this.setErrors([
          {
            message: errroMessage,
            delay: DOWNLOAD_NOTIFICATION_DELAY,
          },
        ]);
      }
    }
  }

  public async downloadMediaFileToGallery(path: string, mediaType: MediaType) {
    const identifierAlbum = await this.getIdentifierAlbum();

    if (identifierAlbum) {
      await this.downloadMediaFile(path, identifierAlbum, mediaType);
      return;
    }

    await Media.createAlbum({ name: ALBUM_NAME });

    const createdIdentifierAlbum = await this.getIdentifierAlbum();

    if (createdIdentifierAlbum) {
      await this.downloadMediaFile(path, createdIdentifierAlbum, mediaType);
    }
  }

traci

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Android - Newly created (empty) albums don't show up in getAlbums()

Describe the bug

On android, the media.getAlbums() call returns an empty albums array. Then media.createAlbum({ name }) returns the error message: "Album already exists"

To Reproduce

Here is the code I am using (running on a physical android device). Perhaps I am using the API incorrectly? But I would expect this code to get an album and create it if it does not already exist.

class MyClass {
 
  private static readonly ALBUM_NAME: string = "My Album";

  private readonly media = new Media();

  public async getAlbum(retires: number = 3): Promise<MediaAlbum> {
    logger.debug(`Getting album`);
    const name = MyClass.ALBUM_NAME;
    const response = await this.media.getAlbums();
    const albums = response.albums;
    logger.debug(`Found ${albums.length} albums`);  // This will always show 0 albums returned
    const album = albums.find((a) => a.name === name);
    if (album !== undefined) {  // Since 0 albums are returned, this statement never triggers
      return album;
    }

    try {
      logger.debug(`Creating album ${name}`);
      await this.media.createAlbum({ name });  // A new album is created, but...
    } catch (e) {
      logger.error(`Failed to create album`);  // It always fails
      logger.error(JSON.stringify(e));  // with the message: "Album already exists"
    }

    // A retry is triggered, but each attempt behaves the same until it eventually gives up.
    if (retires > 0) {
      return this.getAlbum(retires - 1);
    } else {
      throw new Error(`MyClass.getAlbum: retries exceeded`);
    }
  }

}

Expected behavior

I would expect the code above to get the media album named "My Album" and create it if it does not already exist.

Desktop (please complete the following information):

  • OS: MacOS Catalina

Smartphone (please complete the following information):

  • Device: Galaxy J3 Orbit (Android)
  • OS: Android 8.0.0
  • Browser: Hosted in Capacitor version 2.0

Additional context
The same plugin and code is functioning properly on an iPhone.

Is there any method to retrieve more photos like using offset in query?

Is your feature request related to a problem? Please describe.
I'm trying to build custom gallery to enable users to pick photos from.

Describe the solution you'd like
photos must be loaded constantly as users navigate through gallery so there must be a method to load more photos using cursor token or offset.
Is there any way to achieve this using this plugin?

Permissions requested for saving are too broad

Is your feature request related to a problem? Please describe.

Calling savePhoto on iOS requests the user to allow access to their entire photo library, or select certain photos.

This doesn't make sense because savePhoto is write only.

iOS has a "Allow Photos Access: Add Photos Only" permission. This would be much more appropriate to use.

Describe the solution you'd like

savePhoto only requests/uses add photos permission.

Describe alternatives you've considered

N/A

i cant save image use it

To Native -> MediaPlugin savePhoto 116552656
ERROR MESSAGE: {"message":"Unable to save image to album","errorMessage":"The operation couldn’t be completed. (Cocoa error -1.)"}
⚡️ [error] - ERROR Error: Uncaught (in promise): Object: {"message":"Unable to save image to album","errorMessage":"The operation couldn’t be completed. (Cocoa error -1.)"}

feat(android): allow URLs with no extensions to be used for image download

File: MediaPlugin
Line: 331
Code fragment: String extension = absolutePath.substring(absolutePath.lastIndexOf("."));

Explanation.
copyFile method expects to have the absolute path with expansion, but there are cases when the path is without expansion, for example https://domain/core/images/ec432ec1-e203-419c-948c-6b4b0146ed5f/download.

On IOS this case works correctly, no issues.

Read file path from platform-specific identifier on iOS

Is your feature request related to a problem? Please describe.
On Android, the Media.savePhoto method returns with the saved file's path. On iOS, the savePhoto response is void.
To get the saved file's path on iOS, I tried to use the getMedias method to list the files in my gallery subfolder and pick the latest one (which is the saved file). It works fine, but the file object contains only the base64 data and the identifier property, which is the file's platform-specific identifier.

Describe the solution you'd like
Can the file path be retrieved from the platform-specific identifier?

Capacitor V4 Android getAlbums() returns an empty array

Describe the bug
On a Google Pixel 6 Capacitor V4 app and trying to get the albumIdentifier, I get an empty array without any album names. This results in the savePhoto function not working. Is this a bug or is there a way around this issue?

To Reproduce
Steps to reproduce the behavior:

  1. Calling the getAlbums() results in an empty array

Expected behavior
Calling the getAlbums() should return a list of available albums

Smartphone (please complete the following information):

  • Device: Google Pixel 6
  • OS: Android 13
  • Version Capacitor V4

Custom filenames

Is your feature request related to a problem? Please describe.
Can't savePhoto() with a custom filename. It forces generic IMG_... filename, regardless of inputFile.

Describe the solution you'd like
In MediaPlugin.java, replace "IMG_" + timeStamp + extension with absolutePath.substring(absolutePath.lastIndexOf("/")+1) i.e. instead of using a generic IMG_... filename, just use the name of the file provided as source. This could be under a conditional checking for a boolean flag custom passed from savePhoto()_saveMedia()→copyFile()`.

Describe alternatives you've considered
Renaming the output file, but this feels like a horrible idea. I just made a fork for myself with forcing inputFile filename (no condition), but it'd be nice if this was in-built. This is pretty basic functionality, I'd think.

Get Videos

Is your feature request related to a problem? Please describe.
There doesn't appear to be a way yet to get video's in capacitor. There is the getPhoto() method in the capacitor camera plugin but no getVideo(). Even if it just returns a file URL, this would be super useful.

Describe the solution you'd like
A getVideos() method that opens the OS Videos to be able to select a video and it return the video in say blob format or the file location

Describe alternatives you've considered
I have used to Cordova camera plugin to achieve this however there are some issues when building it in android.

Additional context
I can provide more details upon request, hopefully the above makes sense.

Updating to Capacitor 4

When updating to media to version 4 for use with Capacitor 4 I'm getting an error: no matching version found for @capacitor-community/[email protected].

What is the best way to migrate this plugin to be used with Capacitor 4?

Thank you.

doc/ios: app will crash when NSPhotoLibraryUsageDescription key is missing

Describe the bug
When NSPhotoLibraryUsageDescription key is not set on info.plist the app will crash(on a new install) when calling any method. It would be good to have this information on the introduction/doc for IOS.

Error:
APP_NAME[8149:1925821] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.

I'll make a PR to add this information on the readme.

To Reproduce
Steps to reproduce the behavior:

  1. Make a fresh install of the app without NSPhotoLibraryUsageDescription key on info.plist
  2. Call any method of the plugin

Expected behavior
Improve documentation to warn users about this.

Screenshots
n/a

Desktop (please complete the following information):
n/a

Smartphone (please complete the following information):

  • Device: iPhone 12
  • OS: 16.1.2
  • Capacitor v4
  • Media v4

Additional context
n/a

Android: RuntimeException occurred on savePhoto()

Describe the bug
When trying to save photo on android, the function return erorr "RuntimeException occurred".

Expected behavior
Saving image to gallery

Error: RuntimeException occurred at returnResult (vehicles_worldwide_in_2022:717:32) at win.androidBridge.onmessage (vehicles_worldwide_in_2022:692:21)

Media gets deleted when uninstalling app

I save photos with Media.savePhoto(...). They show up in the album but when I uninstall the app, the album gets deleted. Is this the expected behavior and is there a way to disable it? (Using Android, not sure about iOS)

Android 10 - Serious error executing plugin - Invalid column DISTINCT bucket_display_name

I have upgraded to:

    "@capacitor/android": "2.4.2",
    "@capacitor/core": "2.4.2",
    "@capacitor/ios": "2.4.2",
    "@capacitor-community/media": "^1.0.1",

When running my app on Android 10 and executing the following:

await media.getAlbums();

I get the following error In debugger:

D/DEBUG LOG: GET ALBUMS
D/DEBUG LOG: HAS PERMISSION
    ___GET ALBUMS
E/Capacitor: Serious error executing plugin
    java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
        at com.getcapacitor.Bridge$1.run(Bridge.java:526)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.IllegalArgumentException: Invalid column DISTINCT bucket_display_name
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
        at android.content.ContentProviderProxy.query(ContentProviderNative.java:423)
        at android.content.ContentResolver.query(ContentResolver.java:944)
        at android.content.ContentResolver.query(ContentResolver.java:880)
        at android.content.ContentResolver.query(ContentResolver.java:836)
        at com.getcapacitor.community.media.MediaPlugin._getAlbums(MediaPlugin.java:62)
        at com.getcapacitor.community.media.MediaPlugin.getAlbums(MediaPlugin.java:46)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99) 
        at com.getcapacitor.Bridge$1.run(Bridge.java:526) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
E/AndroidRuntime: FATAL EXCEPTION: CapacitorPlugins
    Process: couk.userquest.app, PID: 6448
    java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
        at com.getcapacitor.Bridge$1.run(Bridge.java:535)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
        at com.getcapacitor.Bridge$1.run(Bridge.java:526)
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
     Caused by: java.lang.IllegalArgumentException: Invalid column DISTINCT bucket_display_name
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
        at android.content.ContentProviderProxy.query(ContentProviderNative.java:423)
        at android.content.ContentResolver.query(ContentResolver.java:944)
        at android.content.ContentResolver.query(ContentResolver.java:880)
        at android.content.ContentResolver.query(ContentResolver.java:836)
        at com.getcapacitor.community.media.MediaPlugin._getAlbums(MediaPlugin.java:62)
        at com.getcapacitor.community.media.MediaPlugin.getAlbums(MediaPlugin.java:46)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99) 
        at com.getcapacitor.Bridge$1.run(Bridge.java:526) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 

I have traced the problem back to this line of code: https://github.com/capacitor-community/media/blob/master/android/src/main/java/com/getcapacitor/community/media/MediaPlugin.java#L61

When I remove "DISTINCT " + the problem is solved, apparently this is no longer permitted in Android 10 (https://stackoverflow.com/questions/58601599/illegalargumentexception-invalid-column-distinct-bucket-display-name)

Is there an alternative solution for "DISTINCT"? Can this simply be removed? If so I can create a pull request.

add more info in README on what this does

What I can gather from the readme:

Capacitor community plugin for enabling extra media capabilities

and it can do stuff like

savePhoto
saveVideo

But it's not very clear right now if this relates to capturing photos/videos from the camera as well?
"save photo" is such a vague term, what does it mean EXACTLY?

Is it like save a photo from a URL or from the iPhone/Android local storage? Or save a photo that you capture?

I'd love some extra information on what this plugin does exactly!

Permission in ios

If permission has just only for selected Photos then It create album but not return in getAlbums
and that's so on when I save another photo and it create every time new album

Android : Error: Album already exists

Describe the bug
Writing Video to Album in Android Gallery fails with the message "Error: Album already exists".
This happens the first time the user tries writing content to the Gallery.
Same code works for iOS.

To Reproduce

  • Check Album exists
  • lf album does not exist, call Media.createAlbum() and return new Album
  • Call Media.saveVideo()

await writeVideoToGallery('path/to/my/blob')

writeVideoToGallery = path => {
    return new Promise(async (resolve, reject) => {
      const album = await this.getAlbum()
      const platform = Capacitor.getPlatform()
      Media.saveVideo({ path, album })
        .then(resolve)
        .catch(err => { 
          reject(err)
        })
    })
  }


  getAlbum = async () => {
    return new Promise(async (resolve, reject) => {
      const albumsObj = await Media.getAlbums() 
      const albumName = this.albumName
      let albumFound
      if (albumsObj.albums) {
        let albums = albumsObj.albums.filter(x => x.name === albumName)
        if (albums.length < 1) { 
          console.log('Creating Album...')
          await Media.createAlbum({ name: albumName })
          albums = (await Media.getAlbums()).albums.filter(x => x.name === albumName)
        } else {
          console.log('Album Exists.')
        }

        albumFound = albums[0]
        resolve({ id: albumFound.identifier, name: albumFound.name })
      } else {
        reject('No Albums found')
      }
    })
  }

**Smartphone **

  • Device: Android Galaxy A71
  • OS: Android v12
  • "@capacitor-community/media": "^3.0.0"

Additional context

  • using "@capacitor-community/media": "^3.0.0"
  • code works for iOS

Error

(index):218 native Media.getAlbums (#23824015)
(index):192 result Media.getAlbums (#23824015)
App.3148261f.js:formatted:3188 Creating Album...
(index):218 native Media.createAlbum (#23824016)
(index):192 result Media.createAlbum (#23824016)
(index):200 {message: 'Album already exists'} 
cap.fromNative @ (index):400 
(index):413 Uncaught (in promise) Error: Album already exists
    at cap.fromNative ((index):413:32) 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.