{"id":13561529,"url":"https://github.com/ivandokov/phockup","last_synced_at":"2025-04-03T17:31:09.256Z","repository":{"id":38955393,"uuid":"67368674","full_name":"ivandokov/phockup","owner":"ivandokov","description":"Media sorting tool to organize photos and videos from your camera in folders by year, month and day.","archived":false,"fork":false,"pushed_at":"2024-05-06T22:57:08.000Z","size":246,"stargazers_count":854,"open_issues_count":29,"forks_count":108,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-11-04T13:37:40.277Z","etag":null,"topics":["camera","exiftool","organize-media-files","organize-photos","photobackup","sort-images","sort-media-files","sort-videos","sorting"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivandokov.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-09-04T21:15:52.000Z","updated_at":"2024-11-04T10:28:23.000Z","dependencies_parsed_at":"2024-01-17T16:08:05.225Z","dependency_job_id":"d3781d7e-83ab-4936-ab4d-f07bf287daf6","html_url":"https://github.com/ivandokov/phockup","commit_stats":null,"previous_names":[],"tags_count":58,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivandokov%2Fphockup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivandokov%2Fphockup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivandokov%2Fphockup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivandokov%2Fphockup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivandokov","download_url":"https://codeload.github.com/ivandokov/phockup/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247046859,"owners_count":20874733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["camera","exiftool","organize-media-files","organize-photos","photobackup","sort-images","sort-media-files","sort-videos","sorting"],"created_at":"2024-08-01T13:00:57.928Z","updated_at":"2025-04-03T17:31:04.247Z","avatar_url":"https://github.com/ivandokov.png","language":"Python","funding_links":[],"categories":["Install from Source","Applications","Python","Data curation"],"sub_categories":["Photos","Graphics","Download automation"],"readme":"# Phockup\n\n[![Tests](https://github.com/ivandokov/phockup/actions/workflows/tests.yml/badge.svg)](https://github.com/ivandokov/phockup/actions/workflows/tests.yml)\n[![Deploy](https://github.com/ivandokov/phockup/actions/workflows/deploy.yml/badge.svg)](https://github.com/ivandokov/phockup/actions/workflows/deploy.yml)\n[![Lint](https://github.com/ivandokov/phockup/actions/workflows/lint.yml/badge.svg)](https://github.com/ivandokov/phockup/actions/workflows/lint.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](license)\n\nMedia sorting tool to organize photos and videos from your camera in folders by year, month and day.\n\n## How it works\nThe software will collect all files from the input directory and copy them to the output directory without changing the files content. It will only rename the files and place them in the proper directory for year, month and day.\n\nAll files which are not images or videos or those which do not have creation date information will be placed in a directory called `unknown` without file name change. By doing this you can be sure that the input directory can be safely deleted after the successful process completion because **all** files from the input directory have a copy in the output directory.\n\nIf the target file already exists, its checksum is compared with the source to determine if it is a duplicate. If the checksums are different, we do not have a duplicate and the target filename will be suffixed with a number, for example \"-1\". If the checksums match, the copy operation will be skipped.\n\n## Installation\n\n### Docker\n\nThe docker container supports two operation modes. The first allows for a single execution of phockup. In this mode, the container will be stopped after the execution is complete. The second mode allows for execution in intervals. In this mode, the container will continue running until the user decides to stop it.\n\n#### Single execution mode\nIn this mode, all phockup parameters need to be passed as direct parameters within the docker run command. As you define a complete set of phockup parameters for this execution mode, this includes the paths to the input and output folders within the container.\nTo execute phockup only once, use the following command:\n\n```\ndocker run -v ~/Pictures:/mnt ivandokov/phockup:latest /mnt/input /mnt/output [PHOCKUP ARGUMENTS]\n```\n\n#### Continuous execution mode\nIn this mode, all relevant settings are defined through environment variables and volume mappings. The folders where phockup moves files are always /mnt/input and /mnt/output within the container and can not be changed. You can of course map any folder on your host system to those folders within the container.\n\nThe `-v ~/Pictures/input:/mnt/input` part of the command mounts your `~/Pictures/input` directory to `/mnt/input` inside the container. The same is done for the output folder. You can pass any **absolute** path to be mounted to the container and later on be used as paths for the `phockup` command. The example above provides your `~/Pictures/input` as `INPUTDIR` and `~/Pictures/output` as `OUTPUDIR`. You can pass additional arguments through the `OPTIONS` environment variable.\n\nTo keep the container running and execute phockup in intervals, use the following command:\n\n```\ndocker run -v ~/Pictures/input:/mnt/input -v ~/Pictures/output:/mnt/output -e \"CRON=* * * * *\" -e \"OPTIONS=[PHOCKUP ARGUMENTS]\" ivandokov/phockup:latest\n```\n\nThis will execute phockup once every minute (as defined by the [value of the CRON environment variable](https://crontab.guru/#*_*_*_*_*)). However, the container will not spawn a new phockup process if another phockup process is still running. You can define other intervals for execution using the usual cron syntax. If you want to pass further arguments to phockup, use the OPTIONS environment variable. In this execution mode, phockup will always use the directories mounted to `/mnt/input` and `/mnt/output` and ignore arguments passed in the style of the single execution mode.\n\n### Mac\nRequires [Homebrew](http://brew.sh/)\n```\nbrew tap ivandokov/homebrew-contrib\nbrew install phockup\n```\n\n### Linux (snap)\nRequires [snapd](https://snapcraft.io/docs/core/install)\n```\nsudo snap install phockup\n```\n*Note: snap applications can access files only in your **home and `/media` directories** for security reasons. If your media files are not located in these directories you should use the installation method below.\nIf your files are in `/media` you should run the following command to allow access:*\n```\nsudo snap connect phockup:removable-media\n```\n\n### Linux (without snap)\nIf you are using distro which doesn't support [snapd](https://snapcraft.io/docs/core/install) or you don't want to download the snap you can use the following commands to download the source and set it up\n```\nsudo apt-get install python3 libimage-exiftool-perl -y\ncurl -L https://github.com/ivandokov/phockup/archive/latest.tar.gz -o phockup.tar.gz\ntar -zxf phockup.tar.gz\nsudo mv phockup-* /opt/phockup\ncd /opt/phockup\npip3 install -r requirements.txt\nsudo ln -s /opt/phockup/phockup.py /usr/local/bin/phockup\n```\n\n### Linux (AUR)\n\nIf you are an arch user you can install from the [aur](https://aur.archlinux.org/packages/phockup).\n\nFor example using [yay](https://github.com/Jguer/yay):\n\n```bash\nyay -S phockup\n```\n\n### Windows\n* Download and install latest stable [Python 3](https://www.python.org/downloads/windows/)\n* Download Phockup's [latest release](https://github.com/ivandokov/phockup/archive/latest.tar.gz) and extract the archive\n* Download exiftool from the official [website](https://exiftool.org/) and extract the archive\n* Rename `exiftool(-k).exe` to `exiftool.exe`\n* Move `exiftool.exe` to phockup folder\n* Open Command Prompt and `cd` to phockup folder\n* Use the command below (use `phockup.py` instead of `phockup`)\n\n## Usage\nOrganize photos from one directory into another\n```\nphockup INPUTDIR OUTPUTDIR\n```\n\n`INPUTDIR` is the directory where your photos are located.\n`OUTPUTDIR` is the directory where your **sorted** photos will be stored. It could be a new not existing directory.\n\nExample:\n```\nphockup ~/Pictures/camera ~/Pictures/sorted\n```\n\n### Version\nIf you want to view the version of phockup use the flag `-v | --version`.\n\n### Date format\nIf you want to change the output directories date format you can do it by passing the format as `-d | --date` argument.\nYou can choose different year format (e.g. 17 instead of 2017) or decide\n        to skip the day directories and have all photos sorted in year/month.\n\n```\nSupported formats:\n    YYYY - 2016, 2017 ...\n    YY   - 16, 17 ...\n    MM   - 07, 08, 09 ...\n    M    - July, August, September ...\n    m    - Jul, Aug, Sept ...\n    DD   - 27, 28, 29 ... (day of month)\n    DDD  - 123, 158, 365 ... (day of year)\n    U    - 00, 01, 53 ... (week of the year, Sunday first day of week)\n    W    - 00, 01, 53 ... (week of the year, Monday first day of week)\n\nExample:\n    YYYY/MM/DD -\u003e 2011/07/17\n    YYYY/M/DD  -\u003e 2011/July/17\n    YYYY/m/DD  -\u003e 2011/Jul/17\n    YY/m-DD    -\u003e 11/Jul-17\n    YYYY/U     -\u003e 2011/30\n    YYYY/W     -\u003e 2011/28\n```\n\n### Prefix/Suffix\nIn order to support both aggregation and finer granularity of files\nsorted, you can specify a prefix or suffix (or both) to aid in storing\nfiles in directories beyond strictly date.\n\n*NOTE:* Prefixes and suffixes will also apply to the **'unknown'** folder to\nisolate files that cannot be processed into their respective folders.\nThis creates a bit more chaos for 'unknown' files, but should allow\nthem to be managed by whomever they \"belong\" to.\n\n#### Prefix\n`--output-prefix` flag can be used to specify a directory to be\nappended to the `OUTPUTDIR`, and thus prepended to the date.\n\nFor example:\n```\nphockup ~/Pictures/camera /mnt/sorted --output_prefix=nikon\n```\nwould place files in folders similar to:\n```\n/mnt/sorted/nikon/2011/07/17\n/mnt/sorted/nikon/unknown\n```\n\nWhile it may seem to be redundant with `OUTPUTDIR`, this flag is\nintended to add support for more cleanly determining the output\ndirectory at run-time via environment variable expansion (i.e. use\n$USER, %USERNAME%, $HOSTNAME, etc. to aggregate files)\n\nFor example:\n```\nphockup ~/Pictures/camera /mnt/sorted --output_prefix=$USER\n```\n\nwould yield an output directory of\n```\n/mnt/sorted/ivandokov/2011/07/17\n/mnt/sorted/ivandokov/unknown\n```\n\nThis allows the same script to be deployed to multiple users/machines\nand allows sorting into their respective top level directories.\n\n#### Suffix\n`--output-suffix` flag can be used to specify a directory within the\ntarget date directory for a file.  This allows files to be sorted in\ntheir respective date/time folders while additionally adding a\ndirectory based on the suffix value for additional metadata.\n\nFor example:\n```\nphockup ~/Pictures/DCIM/NIKOND40 /mnt/sorted --output_suffix=nikon\nphockup ~/Pictures/DCIM/100APPLE /mnt/sorted --output_suffix=iphone\n```\n\nThis would allow files to be stored in the following structure:\n\n```\n/mnt/sorted/2011/07/17/nikon/DCS_0001.NEF\n...\n/mnt/sorted/2011/07/17/nikon/DCS_0099.NEF\n/mnt/sorted/unknown/nikon/\n\n/mnt/sorted/2011/07/17/iphone/ABIL6163.HEIC\n...\n/mnt/sorted/2011/07/17/iphone/YZYE9497.HEIC\n/mnt/sorted/unknown/iphone/\n```\n\nThe output suffix also allows for environment variable expansion (e.g.\n$USER, $HOSTNAME, %USERNAME%, etc.) allowing dynamic folders to\nrepresent additional metadata about the images.\n\nFor example:\n\n```\nphockup ~/Pictures/ /mnt/sorted --output_suffix=$HOSTNAME\n\nor\n\nphockup ~/Pictures/ /mnt/sorted --output_suffix=$USER\n```\ncould be used to sort images based on the source computer or user,\nperventing hetrogenous collections of images from disparate sources\nsaving to the same central respository.\n\nThe two options above can be used to help sort/store images\n\n#### Limit files processed by date\n`--from-date` flag can be used to limit the operations to the files that are newer than the provided date (inclusive).\nThe date must be specified in format YYYY-MM-DD. Files with unknown date won't be skipped.\n\nFor example:\n```\nphockup ~/Pictures/DCIM/NIKOND40 ~/Pictures/sorted --from-date=\"2017-01-02\"\n```\n`--to-date` flag can be used to limit the operations to the files that are older than the provided date (inclusive).\nThe date must be specified in format YYYY-MM-DD. Files with unknown date won't be skipped.\n\nFor example:\n```\nphockup ~/Pictures/DCIM/NIKOND40 ~/Pictures/sorted --to-date=\"2017-01-02\"\n```\n\n`--from-date` and `--to-date` can be combined for better control over the files that are processed.\n\nFor example:\n```\nphockup ~/Pictures/DCIM/NIKOND40 ~/Pictures/sorted --from-date=\"2017-01-02\" --to-date=\"2017-01-03\"\n```\n\n### Missing date information in EXIF\nIf any of the photos does not have date information you can use the `-r | --regex` option to specify date format for date extraction from filenames:\n```\n--regex=\"(?P\u003cday\u003e\\d{2})\\.(?P\u003cmonth\u003e\\d{2})\\.(?P\u003cyear\u003e\\d{4})[_-]?(?P\u003chour\u003e\\d{2})\\.(?P\u003cminute\u003e\\d{2})\\.(?P\u003csecond\u003e\\d{2})\"\n```\n\nAs a last resort, specify the `-t | --timestamp` option to use the file modification timestamp. This may not be accurate in all cases but can provide some kind of date if you'd rather it not go into the `unknown` folder.\n\n### Move files\nInstead of copying the process will move all files from the INPUTDIR to the OUTPUTDIR by using the flag `-m | --move`. This is useful when working with a big collection of files and the remaining free space is not enough to make a copy of the INPUTDIR.\n\n### Link files\nInstead of copying the process will create hard link all files from the INPUTDIR into new structure in OUTPUTDIR by using the flag `-l | --link`. This is useful when working with good structure of photos in INPUTDIR (like folders per device).\n\n### Original filenames\nOrganize the files in selected format or using the default year/month/day format but keep original filenames by using the flag `-o | --original-names`.\n\n### File Type\nBy default, Phockup addresses both image and video files. If you want to restrict your command to either images or videos only, use `--file-type=[image|video]`.\n\n### Fix incorrect dates\nIf date extracted from photos is incorrect, you can use the `-f | --date-field` option to set the correct exif field to get date information from. Use this command to list which fields are available for a file:\n```\nexiftool -time:all -mimetype -j file.jpg\n```\nThe output may look like this, but with more fields:\n```\n[{\n  \"DateTimeOriginal\": \"2017:10:06 01:01:01\",\n  \"CreateDate\": \"2017:01:01 01:01:01\",\n]}\n```\nIf the correct date is in `DateTimeOriginal`, you can include the option `--date-field=DateTimeOriginal` to get date information from it.\nTo set multiple fields to be tried in order until a valid date is found, just join them with spaces in a quoted string like `\"CreateDate FileModifyDate\"`.\n\n### Dry run\nIf you want phockup to run without any changes (don't copy/move any files) but just show which changes would be done, enable this feature by using the flag `-y | --dry-run`.\n\n### Log\nIf you want phockup to run and store the output in a log file use the flag `--log`. This flag can be used in conjunction with the flags `--quiet` or `--progress`.\n```\n--log=\u003cPATH\u003e/log.txt\n```\n\n### Quiet run\nIf you want phockup to run without any output (displaying only error messages, and muting all progress messages) use the flag `--quiet`.\n\n### Progress run\nIf you want phockup to run with a progressbar (displaying only the progress and muting all progress messages (including errors)) use the flag `--progress`.\n\n\n### Limit directory traversal depth\nIf you would like to limit how deep the directories are traversed, you can use the `--maxdepth` option to specify the maximum number of levels below the input directory to process.  In order to process only the input directory, you can disable sub-directory processing with:\n`--maxdepth=0`  The current implementation is limited to a maximum depth of 255.\n\n### Improving throughput with concurrency\nIf you want to allocate additional CPUs/cores to the image processing\noperations, you can specify additional resources via the\n`--max-concurrency` flag. Specifying `--max-concurrency=n`, where `n`\nrepresents the maximum number of operations to attempt\nconcurrently, will leverage the additional CPU resources to start\nadditional file operations while waiting for file I/O.  This can lead\nto significant increases in file processing throughput.\n\nDue to how concurrency is implemented in Phockup (specifically\n`ThreadPoolExecutor`), this option has the greatest impact on\ndirectories with a large numbers of files in them,\nversus many directories with small numbers of files in each.  As a\ngeneral rule, the concurrency _should not_ be set higher than the\ncore-count of the system processing the images.\n\n`--max-concurrency=1` has the default behavior of no concurrency while\nprocessing the files in the directories.  Beginning with 50% of the\ncores available is a good start.  Larger numbers can have\ndiminishing returns as the number of concurrent operations saturate\nthe file I/O of the system.\n\nConcurrently processing files does have an impact on the order that\nmessages are written to the console/log and the ability to quickly\nterminate the program, as the execution waits for all in-flight\noperations to complete before shutting down.\n\n## Development\n\n### Running tests\nTo run the tests, first install the dev dependencies using\n\n```bash\npip3 install -r requirements-dev.txt\n```\n\nThen run the tests using\n\n```bash\npytest\n```\n\nTo run the tests with coverage reports run\n```bash\npytest --cov-report term-missing:skip-covered --cov=src tests/\n```\n\nPlease add the necessary tests when committing a feature or improvement.\n\n\n### Pre-commit checks\nWe leverage the [pre-commit](https://pre-commit.com/) framework to automate some general linting/quality checks.\n\nTo install the hooks, from within the activated virtualenv run:\n\n```bash\npre-commit install\n```\n\nTo manually execute the hooks, run:\n\n```bash\npre-commit run -a\n```\n\n### Style Guide Ruleset\nPlease make sure that the code is compliant as described below when committing a feature or improvement.\n\n#### Flake8\nWe use [flake8](https://flake8.pycqa.org/en/latest/) to check the PEP 8 ruleset.\n\nCode style for the line length are following the description of the tool [black](https://black.readthedocs.io/en/stable/the_black_code_style.html#line-length)\nIn a nutshell, this comes down to 88 characters per line. This number was found to produce significantly shorter files.\n\n#### isort\nWe also use [isort](https://github.com/PyCQA/isort) to check if import are sorted alphabetically, separated into sections and by type.\n\n##### single-quotes and double-quotes\nWe try to adhere to the following as much as possible:\nUse single-quotes for string literals, e.g. 'my-identifier', but use double-quotes for strings that are likely to contain single-quote characters as part of the string itself (such as error messages, or any strings containing natural language), e.g. \"You've got an error!\".\n\nSingle-quotes are easier to read and to type, but if a string contains single-quote characters then double-quotes are better than escaping the single-quote characters or wrapping the string in double single-quotes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivandokov%2Fphockup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivandokov%2Fphockup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivandokov%2Fphockup/lists"}