コード例 #1
0
        List <OdFileInfo> SelectFiles(SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            mediaQueue.ReportProgress("Selecting files");

            // Determine the "after" threshold from SelectAfter and SelectIncremental
            var after = sourceConfig.GetBookmarkOrAfter(m_bookmarkPath);

            if (after.HasValue)
            {
                mediaQueue.ReportProgress($"  Filter: Created after {after.Value}");
            }

            // Retrieve file info and enqueue
            var queue = new List <OdFileInfo>();
            int count = 0;

            // This is a Kludge. Need a way to either detect the correct folder or to
            // configure it when setting up a new named source. And, with the named
            // source, we need to clean up the circumstance when credentials expire.
            string nextUrl = m_sourceName.Equals("BrandtOneDrive")
                ? c_oneDriveSamsungCameraUrl : c_oneDriveCameraRollUrl;

            do
            {
                var fileList = FetchJson(nextUrl);
                var root     = fileList.CreateNavigator();
                nextUrl = root.XPVal("/root/a:item[@item='@odata.nextLink']");

                foreach (XPathNavigator node in fileList.CreateNavigator().Select("/root/value/item"))
                {
                    ++count;
                    var odfi = new OdFileInfo(node);

                    if (!after.HasValue ||
                        (odfi.BookmarkDate.HasValue && odfi.BookmarkDate > after.Value))
                    {
                        queue.Add(odfi);
                    }
                }

                mediaQueue.ReportStatus($"  Selected {queue.Count} Skipped {count - queue.Count}");
            }while (!string.IsNullOrEmpty(nextUrl));
            mediaQueue.ReportStatus(null);
            mediaQueue.ReportProgress($"Selected {queue.Count} Skipped {count - queue.Count}");

            if (queue.Count > c_maxBatch)
            {
                // Sort so that we'll keep the oldest ones.
                queue.Sort((a, b) => DateCompare(a.BookmarkDate, b.BookmarkDate));
                queue.RemoveRange(c_maxBatch, queue.Count - c_maxBatch);
                mediaQueue.RequestAnotherBatch = true;
                mediaQueue.ReportProgress($"Batch limited to {queue.Count}");
            }

            return(queue);
        }
コード例 #2
0
        public void RetrieveMediaFiles(SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            // DCIM requires destination directory
            if (string.IsNullOrEmpty(sourceConfig.DestinationDirectory))
            {
                throw new Exception("DCIM source requires -d destination directory.");
            }

            var queue = SelectDcimFiles(sourceConfig, mediaQueue);

            FileMover.CopyOrMoveFiles(queue, sourceConfig, mediaQueue);
            CleanupDcfDirectories(mediaQueue);
        }
コード例 #3
0
        public void RetrieveMediaFiles(SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            // if a destination directory not specified, error
            if (string.IsNullOrEmpty(sourceConfig.DestinationDirectory))
            {
                throw new InvalidOperationException("OneDrive Source requires destination directory -d");
            }

            mediaQueue.ReportProgress($"Connecting to OneDrive: {m_sourceName}");
            m_accessToken = NamedSource.GetOnedriveAccessToken(m_refreshToken);
            var queue = SelectFiles(sourceConfig, mediaQueue);

            DownloadMediaFiles(queue, sourceConfig, mediaQueue);
        }
コード例 #4
0
        public void RetrieveMediaFiles(SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            var queue = SelectFiles(sourceConfig, mediaQueue);

            // if a destination directory was specified, copy or move the files
            if (sourceConfig.DestinationDirectory != null)
            {
                FileMover.CopyOrMoveFiles(queue, sourceConfig, mediaQueue);
            }

            // Else, simply put them in the mediaQueue
            else
            {
                FileMover.EnqueueFiles(queue, mediaQueue);
            }

            // Save the bookmark
            if (m_newestSelection > DateTime.MinValue)
            {
                // Only sets a bookmark if incremental is on.
                sourceConfig.SetBookmark(m_path, m_newestSelection);
            }
        }
コード例 #5
0
        private List <ProcessFileInfo> SelectDcimFiles(SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            if (sourceConfig.SelectIncremental)
            {
                throw new Exception("DCIM source is not compatible with -selectIncremental");
            }
            if (sourceConfig.SelectAfter.HasValue)
            {
                throw new Exception("DCIM source does not support -selectAfter");
            }

            var sourceFolders = new List <string>();

            // Process each removable drive
            foreach (DriveInfo drv in DriveInfo.GetDrives())
            {
                if (drv.IsReady && drv.DriveType == DriveType.Removable)
                {
                    try
                    {
                        // File system structure is according to JEITA "Design rule for Camera File System (DCF) which is JEITA specification CP-3461
                        // See if the DCIM folder exists
                        DirectoryInfo dcim = new DirectoryInfo(Path.Combine(drv.RootDirectory.FullName, c_dcimDirectory));
                        if (dcim.Exists)
                        {
                            // Folders containing images must be named with three digits followed
                            // by five alphanumeric characters. First digit cannot be zero.
                            foreach (DirectoryInfo di in dcim.EnumerateDirectories())
                            {
                                if (di.Name.Length == 8)
                                {
                                    int dirnum;
                                    if (int.TryParse(di.Name.Substring(0, 3), out dirnum) && dirnum >= 100 && dirnum <= 999)
                                    {
                                        sourceFolders.Add(di.FullName);
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {
                        // Suppress the error and move to the next drive.
                    }
                } // If drive is ready and removable
            }     // for each drive

            var queue = new List <ProcessFileInfo>();

            // Add files from each source folder
            foreach (var path in sourceFolders)
            {
                int bookmark = queue.Count;
                mediaQueue.ReportProgress($"Selecting from: {path}");

                DirectoryInfo di = new DirectoryInfo(path);
                foreach (var fi in di.EnumerateFiles())
                {
                    if ((queue.Count % 100) == 0)
                    {
                        mediaQueue.ReportStatus($"Selected: {queue.Count}");
                    }

                    if (MediaFile.IsSupportedMediaType(fi.Extension))
                    {
                        queue.Add(new ProcessFileInfo(fi));
                    }
                }

                if (queue.Count > bookmark)
                {
                    m_dcfDirectories.Add(path);
                }

                mediaQueue.ReportStatus(null);
                mediaQueue.ReportProgress($"   Selected: {queue.Count - bookmark}");
            }

            return(queue);
        }
コード例 #6
0
        public static void CopyOrMoveFiles(List <ProcessFileInfo> queue, SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            string verb = sourceConfig.MoveFiles ? "Moving" : "Copying";

            mediaQueue.ReportProgress($"{verb} media files to working folder: {sourceConfig.DestinationDirectory}.");

            // Sum up the size of the files to be copied
            long selectedFilesSize = 0;

            foreach (var pfi in queue)
            {
                selectedFilesSize += pfi.Size;
            }

            uint startTicks  = (uint)Environment.TickCount;
            long bytesCopied = 0;

            int n = 0;

            foreach (var pfi in queue)
            {
                if (bytesCopied == 0)
                {
                    mediaQueue.ReportStatus($"{verb} file {n + 1} of {queue.Count}");
                }
                else
                {
                    uint ticksElapsed;
                    unchecked
                    {
                        ticksElapsed = (uint)Environment.TickCount - startTicks;
                    }

                    double   bps       = ((double)bytesCopied * 1000.0) / (double)ticksElapsed;
                    double   remaining = (selectedFilesSize - bytesCopied) / bps;
                    TimeSpan remain    = new TimeSpan(((long)((selectedFilesSize - bytesCopied) / bps)) * 10000000L);

                    mediaQueue.ReportStatus($"{verb} file {n + 1} of {queue.Count}. Time remaining: {remain.FmtCustom()} MBps: {(bps / (1024 * 1024)):#,###.###}");
                }

                string dstFilepath = Path.Combine(sourceConfig.DestinationDirectory, Path.GetFileName(pfi.OriginalFilepath));
                MediaFile.MakeFilepathUnique(ref dstFilepath);

                if (sourceConfig.MoveFiles)
                {
                    File.Move(pfi.Filepath, dstFilepath);
                }
                else
                {
                    File.Copy(pfi.Filepath, dstFilepath);
                }
                pfi.Filepath = dstFilepath;
                bytesCopied += pfi.Size;
                ++n;

                // Add to the destination queue
                mediaQueue.Add(pfi);
            }

            TimeSpan elapsed;

            unchecked
            {
                uint ticksElapsed = (uint)Environment.TickCount - startTicks;
                elapsed = new TimeSpan(ticksElapsed * 10000L);
            }

            mediaQueue.ReportStatus(null);
            mediaQueue.ReportProgress($"{verb} complete. {queue.Count} files, {bytesCopied / (1024.0 * 1024.0): #,##0.0} MB, {elapsed.FmtCustom()} elapsed");
        }
コード例 #7
0
        static void ParseCommandLine(string[] args)
        {
            s_sourceConfiguration   = new SourceConfiguration();
            Program.s_photoFinisher = new PhotoFinisher();

            Program.s_photoFinisher.ProgressReported += ReportProgress;
            Program.s_photoFinisher.StatusReported   += ReportStatus;


            try
            {
                if (args.Length == 0)
                {
                    s_operation = Operation.ShowHelp;
                    return;
                }

                for (int i = 0; i < args.Length; ++i)
                {
                    switch (args[i].ToLowerInvariant())
                    {
                    case "-h":
                    case "-?":
                        s_operation = Operation.ShowHelp;
                        return;

                    case "-s":
                        if (s_mediaSource != null)
                        {
                            throw new Exception("CommandLine -s: Can only specify one source.");
                        }
                        s_mediaSource = new FileSource(NextArgument(args, ref i), false);
                        s_operation   = Operation.ProcessMediaFiles;
                        break;

                    case "-st":
                        if (s_mediaSource != null)
                        {
                            throw new Exception("CommandLine -st: Can only specify one source.");
                        }
                        s_mediaSource = new FileSource(NextArgument(args, ref i), true);
                        s_operation   = Operation.ProcessMediaFiles;
                        break;

                    case "-sdcim":
                        if (s_mediaSource != null)
                        {
                            throw new Exception("CommandLine -sdcim: Can only specify one source.");
                        }
                        s_mediaSource = new DcimSource();
                        s_operation   = Operation.ProcessMediaFiles;
                        break;

                    case "-sname":
                        if (s_mediaSource != null)
                        {
                            throw new Exception("CommandLine -sname: Can only specify one source.");
                        }
                        s_mediaSource = NamedSource.GetNamedSource(NextArgument(args, ref i));
                        s_operation   = Operation.ProcessMediaFiles;
                        break;

#if DEBUG
                    case "-copydcim":
                    {
                        Console.WriteLine("Copying test files.");
                        int n = DcimSource.CopyDcimTestFiles();
                        Console.WriteLine($"Copied {n} files from DCIM_Test to DCIM.");
                    }
                    break;
#endif

                    case "-selectafter":
                        s_sourceConfiguration.SelectAfter = NextArgumentAsDate(args, ref i)
                                                            .ResolveTimeZone(TimeZoneInfo.Local).Date;
                        break;

                    case "-selectincremental":
                        s_sourceConfiguration.SelectIncremental = true;
                        break;

                    case "-d":
                    {
                        string dst = NextArgument(args, ref i);
                        if (!Directory.Exists(dst))
                        {
                            throw new ArgumentException($"Destination folder '{dst}' does not exist.");
                        }
                        s_sourceConfiguration.DestinationDirectory = Path.GetFullPath(dst);
                        s_photoFinisher.DestinationDirectory       = s_sourceConfiguration.DestinationDirectory;
                    }
                    break;

                    case "-w":
                        s_waitBeforeExit = true;
                        break;

                    case "-sortby":
                        switch (NextArgument(args, ref i).ToLowerInvariant())
                        {
                        case "y":
                            s_photoFinisher.SortBy = DatePathType.Y;
                            break;

                        case "ym":
                            s_photoFinisher.SortBy = DatePathType.YM;
                            break;

                        case "ymd":
                            s_photoFinisher.SortBy = DatePathType.YMD;
                            break;

                        case "ymds":
                            s_photoFinisher.SortBy = DatePathType.YMDS;
                            break;

                        default:
                            throw new ArgumentException($"Unexpected value for -sortby: {args[i]}.");
                        }
                        break;

                    case "-sort":
                        s_photoFinisher.SortBy = DatePathType.YMD;
                        break;

                    case "-move":
                        s_sourceConfiguration.MoveFiles = true;
                        break;

                    case "-autorot":
                        s_photoFinisher.AutoRotate = true;
                        break;

                    case "-orderednames":
                        s_photoFinisher.SetOrderedNames  = true;
                        s_photoFinisher.SetMetadataNames = false;
                        break;

                    case "-filenamefrommetadata":
                        s_photoFinisher.SetMetadataNames = true;
                        s_photoFinisher.SetOrderedNames  = false;
                        break;

                    case "-metadatafromfilename":
                        s_photoFinisher.MetadataFromFilename = SetMode.SetIfEmpty;
                        break;

                    case "-metadatafromfilenameoverwrite":
                        s_photoFinisher.MetadataFromFilename = SetMode.SetAlways;
                        break;

                    case "-saveoriginalfn":
                    case "-saveoriginalfilename":
                        s_photoFinisher.SaveOriginalFilaname = true;
                        break;

                    case "-setuuid":
                        s_photoFinisher.SetUuid = true;
                        break;

                    case "-transcode":
                        s_photoFinisher.Transcode = true;
                        break;

                    case "-tag":
                        s_photoFinisher.AddKeywords.Add(NextArgument(args, ref i));
                        break;

                    case "-determinedate":
                        s_photoFinisher.AlwaysSetDate = true;
                        break;

                    case "-alltheway":
                        s_photoFinisher.AutoRotate = true;
                        if (!s_photoFinisher.SetMetadataNames)
                        {
                            s_photoFinisher.SetOrderedNames = true;
                        }
                        s_photoFinisher.SaveOriginalFilaname = true;
                        s_photoFinisher.SetUuid       = true;
                        s_photoFinisher.Transcode     = true;
                        s_photoFinisher.AlwaysSetDate = true;
                        break;

                    case "-deduplicate":
                        s_photoFinisher.DeDuplicate = true;
                        break;

                    case "-setdate":
                        s_photoFinisher.SetDateTo = NextArgumentAsDate(args, ref i)
                                                    .ResolveTimeZone(TimeZoneInfo.Local);
                        break;

                    case "-shiftdate":
                    {
                        string timeShift = NextArgument(args, ref i);

                        // If the next argument starts with a sign, then the shift amount is a simple timespan.
                        if (timeShift[0] == '+' || timeShift[0] == '-')
                        {
                            string   s = (timeShift[0] == '+') ? timeShift.Substring(1) : timeShift;
                            TimeSpan ts;
                            if (!TimeSpan.TryParse(s, System.Globalization.CultureInfo.InvariantCulture, out ts))
                            {
                                throw new ArgumentException($"Invalid value for -shiftDate '{timeShift}'.");
                            }

                            s_photoFinisher.ShiftDateBy = ts;
                        }

                        // Else, shift amount is the difference of two times
                        else
                        {
                            if (i + 1 >= args.Length)
                            {
                                throw new ArgumentException("-shiftDate requires two dates or one offset.");
                            }
                            string secondDate = NextArgument(args, ref i);

                            FileMeta.DateTag dtTarget;
                            if (!FileMeta.DateTag.TryParse(timeShift, out dtTarget))
                            {
                                throw new ArgumentException($"Invalid value for -shiftDate '{args[i]}'.");
                            }

                            FileMeta.DateTag dtSource;
                            if (!FileMeta.DateTag.TryParse(secondDate, out dtSource))
                            {
                                throw new ArgumentException($"Invalid value for -shiftDate '{args[i]}'.");
                            }

                            // Resolve timezone if it was ambiguous.
                            dtTarget.ResolveTimeZone(TimeZoneInfo.Local);
                            dtSource.ResolveTimeZone(TimeZoneInfo.Local);

                            // For whatever reason, they might have used different timezones. Take the difference between the UTC versions.
                            s_photoFinisher.ShiftDateBy = dtTarget.DateUtc.Subtract(dtSource.DateUtc);
                        }
                    }
                    break;

                    case "-settimezone":
                    {
                        string tz  = NextArgument(args, ref i);
                        var    tzi = TimeZoneParser.ParseTimeZoneId(tz);
                        if (tzi == null)
                        {
                            throw new ArgumentException($"Invalid value for -setTimezone '{tz}'. Use '-listTimezones' option to find valid values.");
                        }
                        s_photoFinisher.SetTimezoneTo = tzi;
                    }
                    break;

                    case "-changetimezone":
                    {
                        string tz  = NextArgument(args, ref i);
                        var    tzi = TimeZoneParser.ParseTimeZoneId(tz);
                        if (tzi == null)
                        {
                            throw new ArgumentException($"Invalid value for -changeTimezone '{tz}'. Use '-listTimezones' option to find valid values.");
                        }
                        s_photoFinisher.ChangeTimezoneTo = tzi;
                    }
                    break;

                    case "-updatefscreate":
                        s_photoFinisher.UpdateFileSystemDateCreated = true;
                        break;

                    case "-updatefsmod":
                        s_photoFinisher.UpdateFileSystemDateModified = true;
                        break;

                    case "-setwidth":
                        s_photoFinisher.SetWidth = NextArgumentAsInt(args, ref i);
                        break;

                    case "-setheight":
                        s_photoFinisher.SetHeight = NextArgumentAsInt(args, ref i);
                        break;

                    case "-log":
                        s_log = true;
                        break;

                    case "-listtimezones":
                    case "-listtimezone":
                        s_operation = Operation.ListTimeZones;
                        break;

                    case "-listnamedsources":
                        s_operation = Operation.ListNamedSources;
                        break;

                    case "-authonedrive":
                        s_sourceName = NextArgument(args, ref i);
                        s_operation  = Operation.AuthOneDrive;
                        break;

#if DEBUG
                    case "-testmetadatafromfilename":
                        s_testAction   = MediaFile.TestMetadataFromFilename;
                        s_testArgument = NextArgument(args, ref i);
                        s_operation    = Operation.TestAction;
                        break;
#endif

                    default:
                        throw new ArgumentException($"Unexpected command-line argument: {args[i]}");
                    }
                }

                if (s_photoFinisher.SortBy != DatePathType.None && string.IsNullOrEmpty(s_photoFinisher.DestinationDirectory))
                {
                    throw new ArgumentException("'-sort' option requires '-d' destination option.");
                }
            }
            catch (Exception err)
            {
                Console.WriteLine(err.Message);
                Console.WriteLine("Use '-h' for syntax help");
                s_operation = Operation.CommandLineError;
            }
        } // ParseCommandLine
コード例 #8
0
        void DownloadMediaFiles(List <OdFileInfo> queue, SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            mediaQueue.ReportProgress($"Downloading media files from OneDrive to working folder: {sourceConfig.DestinationDirectory}.");
            DateTime newestSelection = DateTime.MinValue;

            // Sum up the size of the files to be downloaded
            long selectedFilesSize = 0;

            foreach (var fi in queue)
            {
                selectedFilesSize += fi.Size;
            }

            uint startTicks      = (uint)Environment.TickCount;
            long bytesDownloaded = 0;

            int n = 0;

            foreach (var fi in queue)
            {
                if (bytesDownloaded == 0)
                {
                    mediaQueue.ReportStatus($"Downloading file {n + 1} of {queue.Count}");
                }
                else
                {
                    uint ticksElapsed;
                    unchecked
                    {
                        ticksElapsed = (uint)Environment.TickCount - startTicks;
                    }

                    double   bps    = ((double)bytesDownloaded * 8000.0) / (double)ticksElapsed;
                    TimeSpan remain = new TimeSpan(((long)((selectedFilesSize - bytesDownloaded) / (bps / 8))) * 10000000L);

                    mediaQueue.ReportStatus($"Downloading file {n + 1} of {queue.Count}. Time remaining: {remain.FmtCustom()} Mbps: {(bps / (1024 * 1024)):#,###.###}");
                }

                string dstFilepath = Path.Combine(sourceConfig.DestinationDirectory, fi.OriginalFilename);
                MediaFile.MakeFilepathUnique(ref dstFilepath);

                FetchFile(fi.Url, dstFilepath);
                bytesDownloaded += fi.Size;
                ++n;

                // Add to the destination queue
                mediaQueue.Add(new ProcessFileInfo(
                                   dstFilepath,
                                   fi.Size,
                                   fi.OriginalFilename,
                                   fi.OriginalDateCreated ?? DateTime.MinValue,
                                   fi.OriginalDateModified ?? DateTime.MinValue));

                if (fi.BookmarkDate.HasValue && newestSelection < fi.BookmarkDate)
                {
                    newestSelection = fi.BookmarkDate.Value;
                }
            }

            TimeSpan elapsed;

            unchecked
            {
                uint ticksElapsed = (uint)Environment.TickCount - startTicks;
                elapsed = new TimeSpan(ticksElapsed * 10000L);
            }

            mediaQueue.ReportStatus(null);
            mediaQueue.ReportProgress($"Download complete. {queue.Count} files, {bytesDownloaded / (1024.0 * 1024.0): #,##0.0} MB, {elapsed.FmtCustom()} elapsed");
            if (newestSelection > DateTime.MinValue)
            {
                if (sourceConfig.SetBookmark(m_bookmarkPath, newestSelection))
                {
                    mediaQueue.ReportProgress($"Bookmark Set to {newestSelection}");
                }
            }
        }
コード例 #9
0
        private List <ProcessFileInfo> SelectFiles(SourceConfiguration sourceConfig, IMediaQueue mediaQueue)
        {
            mediaQueue.ReportProgress($"Selecting {(m_recursive ? "from" : "tree")}: {m_path}");

            // Determine the "after" threshold from SelectAfter and SelectIncremental
            var after = sourceConfig.GetBookmarkOrAfter(m_path);

            m_newestSelection = after ?? DateTime.MinValue;
            var queue        = new List <ProcessFileInfo>();
            int skippedFiles = 0;

            try
            {
                DirectoryInfo di = new DirectoryInfo(m_directory);
                foreach (var fi in di.EnumerateFiles(m_pattern, m_recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
                {
                    if (((queue.Count + skippedFiles) % 100) == 0)
                    {
                        string message = (skippedFiles == 0)
                            ? $"Selected: {queue.Count}"
                            : $"Selected: {queue.Count} Not Selected: {skippedFiles}";
                        mediaQueue.ReportStatus(message);
                    }

                    if (MediaFile.IsSupportedMediaType(fi.Extension))
                    {
                        // Limit date window of files to be selected
                        if (after.HasValue)
                        {
                            var date = MediaFile.GetBookmarkDate(fi.FullName);
                            if (!date.HasValue || date.Value <= after.Value)
                            {
                                ++skippedFiles;
                                continue;
                            }

                            // For this operation, we do everything in localtime because photo
                            // DateTaken metadata is in localtime.
                            // This is after-the-fact but since it's a debugging test that's OK.
                            Debug.Assert(date.Value.Kind == DateTimeKind.Local);

                            if (m_newestSelection < date.Value)
                            {
                                m_newestSelection = date.Value;
                            }
                        }

                        queue.Add(new ProcessFileInfo(fi));
                    }
                }
            }
            catch (Exception err)
            {
                throw new ArgumentException($"Source '{m_path}' not found. ({err.Message})", err);
            }
            mediaQueue.ReportStatus(null);
            mediaQueue.ReportProgress(skippedFiles == 0
                ? $"   Selected: {queue.Count}"
                : $"   Selected: {queue.Count} Not Selected: {skippedFiles}");

            // If SelectIncremental, report the new bookmark
            if (sourceConfig.SelectIncremental && queue.Count > 0)
            {
                Debug.Assert(m_newestSelection > DateTime.MinValue);
                mediaQueue.ReportProgress($"   Newest: {m_newestSelection:yyyy'-'MM'-'dd' 'HH':'mm':'ss}");
            }

            return(queue);
        }