Powershell MTP

Welcome to my new blog!  I wanted my first blog post to be something wholly positive, so I decided to share this Powershell script, in case there is anyone else out there who has struggled with automating the copying or moving of files from their phone.  I find I am regularly cleaning pictures and other files off of my phone, due to the limited space, and for a long time I have wanted to make this process simpler and quicker – a one-click process would be ideal.

After searching online, I discovered this script, but from this page where there is a complaint that this script doesn’t work.  Finding the same problem, I was able to use the provided Powershell and come up with a solution that does the job.

The problem with the script appeared to be in the Get-ChildShellItem function.  The usage explanation in the page says to does the following:

$phone = Get-ChildShellItem | where { $_.name -eq 'a820t' }
Get-ChildShellItem -Path "$($phone.Path)\Phone\DCIM" -Filter '(.jpg)|(.3gp)$'

This just causes the script to hang.  The problem seems to be that even though the Path to the phone can be retrieved, Windows cannot use it to access the folder.

My solution is to get the phone object, then split the path and step down the tree, using the GetFolder member of each item, until we reach the folder object in question.  See the functions, “Get-Phone” and “Get-SubFolder” respectively.

The next problem was how to move the files.  The original script uses the “MoveHere” shell method, passing the path of the source file.  Again, this doesn’t work because Windows doesn’t like the path of the MTP device.  But looking at Microsoft’s website, I noticed that you can pass the folder item (the file COM object) directly.  I do not know if this is the best way to do this (e.g. it can cause a file-copy dialogue to appear), but it works!

So, with that short explanation, here is an example MoveFromPhone.ps1 script.  An example of how to use it (with a command script) is also included below.

This Powershell script is provided ‘as-is’, without any express or implied warranty. In no event will I, the author, be held liable for any damages arising from the use of this script.

Please note again, that if used ‘as-is’, this script will move files from you phone: they will be deleted from the source (phone) and moved to the computer. If you want to copy files instead, you can replace the “MoveHere” function call with “CopyHere” instead. But once again, I can take no responsibility for the use, or misuse, of this script.

# Windows Powershell Script to move a set of files (based on a filter) from a folder
# on a MTP device (e.g. Android phone) to a folder on a computer, using the Windows Shell.
# By Daiyan Yingyu, 19 March 2018, based on the (non-working) script found here:
#   https://www.pstips.net/access-file-system-against-mtp-connection.html
# as referenced here:
#   https://powershell.org/forums/topic/powershell-mtp-connections/
#
# This Powershell script is provided 'as-is', without any express or implied warranty.
# In no event will the author be held liable for any damages arising from the use of this script.
#
# Again, please note that used 'as-is' this script will MOVE files from you phone:
# the files will be DELETED from the source (the phone) and MOVED to the computer.
#
# If you want to copy files instead, you can replace the MoveHere function call with "CopyHere" instead.
# But once again, the author can take no responsibility for the use, or misuse, of this script.</em>
#
param([string]$phoneName,[string]$sourceFolder,[string]$targetFolder,[string]$filter='(.jpg)|(.mp4)$')

function Get-ShellProxy
{
	if( -not $global:ShellProxy)
	{
		$global:ShellProxy = new-object -com Shell.Application
	}
	$global:ShellProxy
}

function Get-Phone
{
	param($phoneName)
	$shell = Get-ShellProxy
	# 17 (0x11) = ssfDRIVES from the ShellSpecialFolderConstants (https://msdn.microsoft.com/en-us/library/windows/desktop/bb774096(v=vs.85).aspx)
	# => "My Computer" — the virtual folder that contains everything on the local computer: storage devices, printers, and Control Panel.
	# This folder can also contain mapped network drives.
	$shellItem = $shell.NameSpace(17).self
	$phone = $shellItem.GetFolder.items() | where { $_.name -eq $phoneName }
	return $phone
}

function Get-SubFolder
{
	param($parent,[string]$path)
	$pathParts = @( $path.Split([system.io.path]::DirectorySeparatorChar) )
	$current = $parent
	foreach ($pathPart in $pathParts)
	{
		if ($pathPart)
		{
			$current = $current.GetFolder.items() | where { $_.Name -eq $pathPart }
		}
	}
	return $current
}

$phoneFolderPath = $sourceFolder
$destinationFolderPath = $targetFolder
# Optionally add additional sub-folders to the destination path, such as one based on date

$phone = Get-Phone -phoneName $phoneName
$folder = Get-SubFolder -parent $phone -path $phoneFolderPath

$items = @( $folder.GetFolder.items() | where { $_.Name -match $filter } )
if ($items)
{
	$totalItems = $items.count
	if ($totalItems -gt 0)
	{
		# If destination path doesn't exist, create it only if we have some items to move
		if (-not (test-path $destinationFolderPath) )
		{
			$created = new-item -itemtype directory -path $destinationFolderPath
		}

		Write-Verbose "Processing Path : $phoneName\$phoneFolderPath"
		Write-Verbose "Moving to : $destinationFolderPath"

		$shell = Get-ShellProxy
		$destinationFolder = $shell.Namespace($destinationFolderPath).self
		$count = 0;
		foreach ($item in $items)
		{
			$fileName = $item.Name

			++$count
			$percent = [int](($count * 100) / $totalItems)
			Write-Progress -Activity "Processing Files in $phoneName\$phoneFolderPath" `
				-status "Processing File ${count} / ${totalItems} (${percent}%)" `
				-CurrentOperation $fileName `
				-PercentComplete $percent

			# Check the target file doesn't exist:
			$targetFilePath = join-path -path $destinationFolderPath -childPath $fileName
			if (test-path -path $targetFilePath)
			{
				write-error "Destination file exists - file not moved:`n`t$targetFilePath"
			}
			else
			{
				$destinationFolder.GetFolder.MoveHere($item)
				if (test-path -path $targetFilePath)
				{
					# Optionally do something with the file, such as modify the name (e.g. removed phone-added prefix, etc.)
				}
				else
				{
					write-error "Failed to move file to destination:`n`t$targetFilePath"
				}
			}
		}
	}
}

To call the script you need to supply:

  • the name of the phone (or MTP device).  This is as it appears in Windows Explorer, and is usually the name you have given to the device in its settings
  • the path to the folder in the device.  Usually starts with something like “Phone” or “Internal shared storage”, etc.
  • the fully-qualified path to the folder on the computer to where you want to move the files.
  • a regular expression for all the files you want to move.  For example, for images and videos you might want ‘(.jpg)|(.mp4)$’

You can put the commands into a batch (command) script to move the files from several locations.  Here is an example:

 

@echo off
REM Example use of MoveFromPhone.ps1 to move pictures from a phone onto computer
REM
REM This Windows Command Line script is provided 'as-is', without any express or implied warranty.
REM In no event will the author be held liable for any damages arising from the use of this script.
REM
REM Again, please note that when used with the 'MoveFromPhone.ps1' as originally written,
REM files will be MOVED from you phone: they will be DELETED from the sourceFolder (the phone)
REM and MOVED to the targetFolder (on the computer).
REM
powershell Set-ExecutionPolicy RemoteSigned -scope currentuser
REM Camera files
powershell.exe "& '%~dp0MoveFromPhone.ps1' -phoneName 'MyPhone' -sourceFolder 'Internal shared storage\DCIM\Camera' -targetFolder 'C:\Users\Public\Pictures\Camera' -filter '(.jpg)|(.mp4)$'"
REM Facebook
powershell.exe "& '%~dp0MoveFromPhone.ps1' -phoneName 'MyPhone' -sourceFolder 'Internal shared storage\DCIM\Facebook' -targetFolder 'C:\Users\Public\Pictures\Facebook' -filter '(.jpg)|(.mp4)$'"
REM Screenshots
powershell.exe "& '%~dp0MoveFromPhone.ps1' -phoneName 'MyPhone' -sourceFolder 'Internal shared storage\DCIM\Screenshots' -targetFolder 'C:\Users\Public\Pictures\Screenshots' -filter '(.png)$'"
pause

Please feel free to comment if you spot any mistakes or have any ideas or suggestions for improvements.

34 thoughts on “Powershell MTP

  1. Thank you. It is a very nice script

    I have a question:
    is it possible to retrieve modifieddate of the picture on the phone?
    because I like to move/copy files after a certain datetime.

    1. Hi Paul,
      Thank you for your comment. I am sorry I didn’t spot it earlier. I haven’t been checking, because most comments are spam. Yours was the only one that even mentioned the content of the post.

      Perhaps you have already got the answer. I did not post the full script that I use, because I wanted to focus on the actual method of copying or moving the files, but in fact my own script does indeed sort pictures by date/time.

      I did not find a way to get the information from the file while it is on the phone, so it is copied or moved to a location on the computer, then the information obtained from the file there, a new name/location determined and the file moved (renamed) to the new location.

      I first attempt to get the date taken property from within the picture itself. If that fails (i.e, no date-taken tag is available), get the modified time of the file (the original modified time should have been maintained by the copy or move, so this does seem to work for me).

      So I have two functions, which take the path of the file on the computer. The first is called, and the second called if the first returns an empty string:

      function GetPictureDateTakenString
      {
      param([string]$filePath)
      $dateTakenString = ”

      $OldErrorActionPref = $ErrorActionPreference
      $ErrorActionPreference = “SilentlyContinue”
      $pic = New-Object System.Drawing.Bitmap($filePath)
      if ($pic)
      {
      $dateTakenBytes = $pic.GetPropertyItem(36867).Value
      if ($dateTakenBytes)
      {
      $dateTakenString = [System.Text.Encoding]::ASCII.GetString($dateTakenBytes)
      $dateTaken = [datetime]::ParseExact($dateTakenString,”yyyy:MM:dd HH:mm:ss`0″,$Null)
      $dateTakenString = $dateTaken.tostring(“yyyyMMdd_HHmmss”)
      }
      $pic.Dispose()
      $pic = $null
      }
      $ErrorActionPreference = $OldErrorActionPref
      return $dateTakenString
      }

      function GetModifiedFileTimeString
      {
      param([string]$filePath)
      $lastModifiedDate = (Get-Item $filePath).LastWriteTime
      $dateTimeModifiedString = $lastModifiedDate.tostring(“yyyyMMdd_HHmmss”)
      return $dateTimeModifiedString
      }

      I hope that’s helpful, even if it is rather late.

  2. Nice! But, having played with the shell object some, I can suggest a slight simplification in the Get-Phone function. The folder object returned the Namespace method is identical to $ShellItem.GetFolder. Try the following:

    $Shell = new-object -comobject Shell.Application
    $Shell.Namespace(17).Items | select name
    $Shell.Namespace(17).self.GetFolder.Items() | select name
    $shell.namespace(17) | gm
    $Shell.Namespace(17).Self.GetFolder | gm

    Peace!

    1. I don’t quite understand what you are trying to achieve there.
      The GetPhone method returns the folder object representing the supplied phone name. By doing “select name”, you’ll only return the name, which we already know. The code I supplied is making similar calls, but returning the phone object for iterating through the folders in the phone.
      Could you explain more where you think it could be simplified?

  3. Is there any possibility to use the “-Recurse”?
    Similar to there:
    “Get-ChildItem $sourceFolder -Recurse -Include *.jpg, *.raw, *.jpeg”
    I starded in this way
    It seemed very simple and it worked until I tested with my phone.
    I also used the folder tree based on the Year and Month.

    1. Unfortunately, using Windows to access files through MTP doesn’t have that feature. This is the whole purpose of this script. If you know of another way via the USB connection, feel free to share it.

      One other possible solution is to use FTP . If your phone is connected via wi-fi on a shared network with the computer to which you want to move the files, some phone File Manager apps can configure a FTP point. I guess it could be possible to write a script to automate getting files in that way. However, even whether that has “Get-ChildItem” or “-recurse” I don’t know. It might be faster than MTP.

  4. Good blog you have got here.. It’s hard to find excellent writing like yours nowadays. I really appreciate individuals like you! Take care!!|

  5. After examine just a few of the blog posts on your website now, and I really like your means of blogging. I bookmarked it to my bookmark website list and will be checking back soon. Pls check out my web page as nicely and let me know what you think.

    1. You’re very kind. I had hoped to be more active than I have been, but I’ve been working on other projects.

  6. I’m gone to inform my little brother, that he should also go to see this weblog on regular basis to get updated from latest information.|

  7. This is very fascinating, You are an excessively skilled blogger. I’ve joined your feed and look ahead to looking for extra of your excellent post. Also, I have shared your website in my social networks|

  8. I appreciate, cause I found just what I was having a look for.
    You’ve ended my four day long hunt! God Bless you man.
    Have a nice day. Bye

  9. thanks for the code, i am now using it for backingup files from my iphone, real easy to use, i used to use file explorer to drag files from iphone folder and now can have it automated

    one point need your advice, if u encountered before, i found iphone will become disconnected when copying big files like mov, it also happen before when i am using file explorer, so it is not your script issue, maybe some general iphone connectivitiy issue, btw, it happens at random, some mov file can be copied but some cannot…

    i now use your script and filtering out those mov files, all other jpg/mp4 can be copied.

    pls comment, thanks again

    1. Thanks for the compliment, Michael.
      I have never used an iPhone, so I didn’t even know this would work with one!

      I think the only time I’ve had a similar problem is if the phone disconnects from the computer. The connection on my phone and/or computer is not tight, or the cable might be slightly “iffy”. Basically, to avoid this problem, I simply make sure the connection is in a stable state – connected, phone switched to Transfer Files mode, and computer has detected it – before running the script. And then, make sure not to move the phone (or computer), or touch the cable, etc, until the task has completed.

  10. Another issue I have is that it seems apps on the phone sometimes keep files “locked” with one of two results:

    1) the file is seen by the computer, but cannot be copied/moved; in which case the script throws up an error dialogue which you have to clear before it continues. I haven’t tried to fix this, because it’s kinda useful to know there’s been a problem. I can kill the script and run it again after sorting out the issue.

    2) the file is not seen by the computer at all (even though it can be seen in a file browser on the phone)

    The only way to solve either of these issues is make sure all apps are shut down on the phone (as far as possible) and then restart the phone. Sometimes even this hasn’t solved the “invisible file” problem, and I have to manually move the files on the phone to another folder or to the computer directly.

  11. I will right away grasp your rss as I can not to find your email subscription link or e-newsletter service. Do you’ve any? Please allow me realize in order that I may subscribe. Thanks.|

    1. Thank you for your kind words. I don’t have any kind of subscription. I didn’t know I have an RSS feed even! 😀 I’m sorry I haven’t written much on this blog yet. I am considering recreating some past posts at first, but I need to consider the purpose(s) of this blog to take it forward. In the meantime I have been working on other projects, and just hope to be able to get around to doing something with this blog in the not too distant future.

  12. In line 59 of your script:
    Write-Verbose “Copying to : $destinationFolderPath”
    You explicitly send to powershell the word “Copying”. These files are not being copied, they are being moved. Do you think this can cause a problem for anyone using your script? Personally, this has done irreparable damage. Fix your UX.

    1. Thanks for the heads up. I have corrected it in the text and script file.

      I am not sure why that got left in. I know in my early days of experimenting with this I was copying files instead of moving them (for obvious reasons), and one script I still use gives the option, with a function “CopyOrMoveFiles” (how that begins is shown at the end of this comment). The script in this post was one I edited from my original scripts to share with others to show how to do this, because my scripts do a lot more than this. I guess the Copy got left in from somewhere by mistake.

      I can only apologise. I hope you are able to put the files back. I hope you don’t hold me responsible for any loss. I would point at that, aside from that text (which is only displayed with verbose output on) and a comment, I believe it is pretty clear that the script is written to move the files:

      1) it says so in my post – in fact “Move” appeared 14 times in the post, while “copy” appeared only four times; the paragraph before the script clearly says “The next problem was how to move the files. The original script uses the “MoveHere” shell method”. Furthermore I mentioned in the first paragraph that my purpose was for “cleaning pictures and other files off of my phone”.

      2) it says so in the comment at the top of the script: “Windows Powershell Script to move a set of files”.

      3) the function on the shell folder that does the job is called “MoveFile”: “GetFolder.MoveHere” is fairly prominent, as it appears in black amongst brown text.

      4) BOTH example script files are called “MoveFromPhone” (MoveFromPhone.ps1 and MoveFromPhone.cmd), the command script calls the Powershell script and it has a comment at the top that says “Example use of MoveFromPhone.ps1 to move pictures from a phone”.

      So, in short, I cannot believe anyone would not notice any of these references “to moving files” before running the script, which is when one is most likely to first notice the reference to “Copying”. But again apologise if this was you.

      I think now I will add a disclaimer, and also make it more prominent that it is moving the files.

      As mentioned above, this is how my “CopyOrMoveFiles” function starts:

      function CopyOrMoveFiles
      {
      param($folder,[string]$destinationFolderPath,[switch]$moveFiles=$false,[string]$filter=$null,[int]$progressParentId=-1)

      $copyText = ‘copy’
      $copyingText = ‘Copying’
      $copiedText = ‘copied’

      if ($moveFiles)
      {
      $copyText = ‘move’
      $copyingText = ‘Moving’
      $copiedText = ‘moved’
      }
      # …

  13. Thank you for some other excellent article. Where else could anybody get that type of info
    in such an ideal manner of writing? I have a presentation subsequent week, and I’m at the look for such information.

  14. Wonderful blog! Do you have any recommendations for aspiring writers?
    I’m hoping to start my own site soon but I’m a little lost on everything.
    Would you suggest starting with a free platform like WordPress or go for a
    paid option? There are so many choices out there that I’m
    completely overwhelmed .. Any ideas? Appreciate it!

    1. Hi Carol,
      Thank you for your kind words. I looked at your page; it’s all in Chinese, but says you are from Sweden. When and why did you start learning Chinese?

      As to your question, I don’t know which is best. I pay for this domain and web-space, using WordPress but mainly because I wanted a number of sub-domains, several of which host blogs with a lot of images and videos, but I am sure if you just want a single site I guess you can use wordpress.com directly, which I think is free, or if you just want a blog use something like blogspot.com if it still exists. Having your own domain gives you much more control though, and also means you have e-mail accounts and so on.

  15. You ought to be a part of a contest for one of the highest quality blogs on the internet. I’m going to highly recommend this site!

  16. I’m amazed, I must say. Seldom do I come across a blog that’s equally educative and engaging, and let me tell you, you have hit the nail on the head. The problem is something not enough folks are speaking intelligently about. Now i’m very happy I stumbled across this during my hunt for something concerning this.

  17. Howdy! I could have sworn I’ve been to your blog before but after going through many of the articles I realized it’s new to me. Regardless, I’m definitely happy I discovered it and I’ll be bookmarking it and checking back frequently!

  18. This design is steller! You obviously know how to keep a reader amused.
    Between your wit and your videos, I was almost moved to start my own blog (well,
    almost…HaHa!) Great job. I really enjoyed what you had to say,
    and more than that, how you presented it. Too cool!

  19. After looking over a few of the articles on your web site, I honestly like your
    technique of blogging. I book marked it to my bookmark site list and will be checking
    back soon. Please check out my website as well and let me know your opinion.

  20. I have been exploring for a little for any high quality articles
    or weblog posts on this sort of space . Exploring in Yahoo I at last stumbled upon this web
    site. Reading this information So i’m glad to express
    that I’ve a very good uncanny feeling I came upon exactly what I needed.

  21. Hey, I think your blog might be having browser compatibility issues.
    When I look at your website in Ie, it looks fine but when opening in Internet Explorer, it has some overlapping.
    I just wanted to give you a quick heads up! Other then that, fantastic blog!

    1. Thanks for the heads up, but I don’t know what browser you are referring to: “IE” is Internet Explorer. I just checked it using IE on Windows 8.1 and it looks fine, but IE really is a “dead” browser now anyway; it’s no longer being developed. As a standard up-to-date WP website, I hope the site works okay on all modern browsers. Cheers.

Leave a Reply to Rex Mcilwain Cancel reply

Your email address will not be published. Required fields are marked *