Compare commits
6 Commits
feature_co
...
v0.59.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d31a152be | ||
|
|
34fbd87fc6 | ||
|
|
3ddfea6a3d | ||
|
|
8b59615ff7 | ||
|
|
c590a16d70 | ||
|
|
36ce86c4a6 |
@@ -531,6 +531,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"ideas"
|
"ideas"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "cclauss",
|
||||||
|
"name": "Christian Clauss",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/3709715?v=4",
|
||||||
|
"profile": "https://www.patreon.com/cclauss",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.59.0
|
current_version = 0.59.1
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||||
serialize = {major}.{minor}.{patch}
|
serialize = {major}.{minor}.{patch}
|
||||||
|
|
||||||
|
|||||||
7
.github/workflows/tests.yml
vendored
7
.github/workflows/tests.yml
vendored
@@ -3,6 +3,13 @@ name: Tests
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
ruff: # https://beta.ruff.rs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: pip install --user ruff
|
||||||
|
- run: ruff --format=github --line-length=7228 --target-version=py39
|
||||||
|
--ignore=E402,E712,E721,E722,E741,F401,F403,F405,F541,F601,F811,F821,F822,F841 .
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||||
|
|||||||
@@ -2456,7 +2456,7 @@ cog.out(get_template_field_table())
|
|||||||
|{cr}|A carriage return: '\r'|
|
|{cr}|A carriage return: '\r'|
|
||||||
|{crlf}|A carriage return + line feed: '\r\n'|
|
|{crlf}|A carriage return + line feed: '\r\n'|
|
||||||
|{tab}|:A tab: '\t'|
|
|{tab}|:A tab: '\t'|
|
||||||
|{osxphotos_version}|The osxphotos version, e.g. '0.59.0'|
|
|{osxphotos_version}|The osxphotos version, e.g. '0.59.1'|
|
||||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||||
|{album}|Album(s) photo is contained in|
|
|{album}|Album(s) photo is contained in|
|
||||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
[](https://pepy.tech/project/osxphotos)
|
[](https://pepy.tech/project/osxphotos)
|
||||||
[](https://www.reddit.com/r/osxphotos/)
|
[](https://www.reddit.com/r/osxphotos/)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors)
|
[](#contributors)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS. You can query the Photos library database — for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc. You can also easily export both the original and edited photos.
|
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS. You can query the Photos library database — for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc. You can also easily export both the original and edited photos.
|
||||||
@@ -2094,7 +2094,7 @@ Substitution Description
|
|||||||
{cr} A carriage return: '\r'
|
{cr} A carriage return: '\r'
|
||||||
{crlf} A carriage return + line feed: '\r\n'
|
{crlf} A carriage return + line feed: '\r\n'
|
||||||
{tab} :A tab: '\t'
|
{tab} :A tab: '\t'
|
||||||
{osxphotos_version} The osxphotos version, e.g. '0.59.0'
|
{osxphotos_version} The osxphotos version, e.g. '0.59.1'
|
||||||
{osxphotos_cmd_line} The full command line used to run osxphotos
|
{osxphotos_cmd_line} The full command line used to run osxphotos
|
||||||
|
|
||||||
The following substitutions may result in multiple values. Thus if specified
|
The following substitutions may result in multiple values. Thus if specified
|
||||||
@@ -2581,7 +2581,7 @@ The following template field substitutions are availabe for use the templating s
|
|||||||
|{cr}|A carriage return: '\r'|
|
|{cr}|A carriage return: '\r'|
|
||||||
|{crlf}|A carriage return + line feed: '\r\n'|
|
|{crlf}|A carriage return + line feed: '\r\n'|
|
||||||
|{tab}|:A tab: '\t'|
|
|{tab}|:A tab: '\t'|
|
||||||
|{osxphotos_version}|The osxphotos version, e.g. '0.59.0'|
|
|{osxphotos_version}|The osxphotos version, e.g. '0.59.1'|
|
||||||
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
|{osxphotos_cmd_line}|The full command line used to run osxphotos|
|
||||||
|{album}|Album(s) photo is contained in|
|
|{album}|Album(s) photo is contained in|
|
||||||
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|
|
||||||
@@ -2702,6 +2702,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/swduncan"><img src="https://avatars.githubusercontent.com/u/2053195?v=4?s=75" width="75px;" alt="Steve Duncan"/><br /><sub><b>Steve Duncan</b></sub></a><br /><a href="#ideas-swduncan" title="Ideas, Planning, & Feedback">🤔</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/swduncan"><img src="https://avatars.githubusercontent.com/u/2053195?v=4?s=75" width="75px;" alt="Steve Duncan"/><br /><sub><b>Steve Duncan</b></sub></a><br /><a href="#ideas-swduncan" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="http://www.projany.com"><img src="https://avatars.githubusercontent.com/u/15144745?v=4?s=75" width="75px;" alt="Ian Moir"/><br /><sub><b>Ian Moir</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aianmmoir" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="http://www.projany.com"><img src="https://avatars.githubusercontent.com/u/15144745?v=4?s=75" width="75px;" alt="Ian Moir"/><br /><sub><b>Ian Moir</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Aianmmoir" title="Bug reports">🐛</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pekingduck"><img src="https://avatars.githubusercontent.com/u/2597142?v=4?s=75" width="75px;" alt="Peking Duck"/><br /><sub><b>Peking Duck</b></sub></a><br /><a href="#ideas-pekingduck" title="Ideas, Planning, & Feedback">🤔</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pekingduck"><img src="https://avatars.githubusercontent.com/u/2597142?v=4?s=75" width="75px;" alt="Peking Duck"/><br /><sub><b>Peking Duck</b></sub></a><br /><a href="#ideas-pekingduck" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://www.patreon.com/cclauss"><img src="https://avatars.githubusercontent.com/u/3709715?v=4?s=75" width="75px;" alt="Christian Clauss"/><br /><sub><b>Christian Clauss</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/commits?author=cclauss" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# Sphinx build info version 1
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
config: 97939ee226e568d1b630529e47e51a47
|
config: e2390789da80d7795777c14960fe23f6
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../genindex.html" /><link rel="search" title="Search" href="../search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>Overview: module code - osxphotos 0.59.0 documentation</title>
|
<title>Overview: module code - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css" />
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="../index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="../index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>osxphotos.exiftool - osxphotos 0.58.1 documentation</title>
|
<title>osxphotos.exiftool - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="../../index.html"><div class="brand">osxphotos 0.58.1 documentation</div></a>
|
<a href="../../index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.58.1 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>osxphotos.photoexporter - osxphotos 0.59.0 documentation</title>
|
<title>osxphotos.photoexporter - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="../../index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="../../index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../genindex.html" /><link rel="search" title="Search" href="../../search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>osxphotos.phototemplate - osxphotos 0.59.0 documentation</title>
|
<title>osxphotos.phototemplate - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="../../_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="../../_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="../../_static/copybutton.css" />
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="../../index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="../../index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="../../search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
@@ -1350,7 +1350,7 @@
|
|||||||
<span class="p">)</span>
|
<span class="p">)</span>
|
||||||
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
|
||||||
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
|
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span>
|
||||||
<span class="sa">f</span><span class="s2">"comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">vals</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">"</span>
|
<span class="sa">f</span><span class="s2">"comparison operators may only be used with values that can be converted to numbers: </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">conditional_value</span><span class="si">}</span><span class="s2">"</span>
|
||||||
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
<span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
||||||
|
|
||||||
<span class="n">predicate_is_true</span> <span class="o">=</span> <span class="kc">False</span>
|
<span class="n">predicate_is_true</span> <span class="o">=</span> <span class="kc">False</span>
|
||||||
@@ -1879,7 +1879,7 @@
|
|||||||
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unhandled template value: </span><span class="si">{</span><span class="n">field</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
|
||||||
|
|
||||||
|
|
||||||
<span class="k">def</span> <span class="nf">get_place_value</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="s2">"PhotoInfo"</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
<span class="k">def</span> <span class="nf">get_place_value</span><span class="p">(</span><span class="n">photo</span><span class="p">:</span> <span class="s2">"PhotoInfo"</span><span class="p">,</span> <span class="n">field</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> <span class="c1"># noqa: F821</span>
|
||||||
<span class="sd">"""Get the value of a 'place' field by attribute</span>
|
<span class="sd">"""Get the value of a 'place' field by attribute</span>
|
||||||
|
|
||||||
<span class="sd"> Args:</span>
|
<span class="sd"> Args:</span>
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ Template Substitutions
|
|||||||
* - {tab}
|
* - {tab}
|
||||||
- :A tab: '\t'
|
- :A tab: '\t'
|
||||||
* - {osxphotos_version}
|
* - {osxphotos_version}
|
||||||
- The osxphotos version, e.g. '0.59.0'
|
- The osxphotos version, e.g. '0.59.1'
|
||||||
* - {osxphotos_cmd_line}
|
* - {osxphotos_cmd_line}
|
||||||
- The full command line used to run osxphotos
|
- The full command line used to run osxphotos
|
||||||
* - {album}
|
* - {album}
|
||||||
|
|||||||
2
docs/_static/documentation_options.js
vendored
2
docs/_static/documentation_options.js
vendored
@@ -1,6 +1,6 @@
|
|||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||||
VERSION: '0.59.0',
|
VERSION: '0.59.1',
|
||||||
LANGUAGE: 'en',
|
LANGUAGE: 'en',
|
||||||
COLLAPSE_INDEX: false,
|
COLLAPSE_INDEX: false,
|
||||||
BUILDER: 'html',
|
BUILDER: 'html',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Template System" href="template_help.html" /><link rel="prev" title="OSXPhotos Tutorial" href="tutorial.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.59.0 documentation</title>
|
<title>OSXPhotos Command Line Interface (CLI) - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.59.0 documentation</title>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Index - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos" href="overview.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>osxphotos 0.59.0 documentation</title>
|
<title>osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="#"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="#"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="#">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotos’s documentation!" href="index.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Tutorial" href="tutorial.html" /><link rel="prev" title="Welcome to OSXPhotos’s documentation!" href="index.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>OSXPhotos - osxphotos 0.59.0 documentation</title>
|
<title>OSXPhotos - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Reference" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Reference" href="reference.html" /><link rel="prev" title="OSXPhotos Template System" href="template_help.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>OSXPhotos Python Package Overview - osxphotos 0.59.0 documentation</title>
|
<title>OSXPhotos Python Package Overview - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.59.0 documentation</title>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Python Module Index - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="OSXPhotos Python Package Overview" href="package_overview.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="OSXPhotos Python Package Overview" href="package_overview.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>OSXPhotos Python Reference - osxphotos 0.59.0 documentation</title>
|
<title>OSXPhotos Python Reference - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.59.0 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/><title>Search - osxphotos 0.59.1 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Python Package Overview" href="package_overview.html" /><link rel="prev" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>OSXPhotos Template System - osxphotos 0.59.0 documentation</title>
|
<title>OSXPhotos Template System - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
@@ -613,7 +613,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
|
<tr class="row-odd"><td><p>{osxphotos_version}</p></td>
|
||||||
<td><p>The osxphotos version, e.g. ‘0.59.0’</p></td>
|
<td><p>The osxphotos version, e.g. ‘0.59.1’</p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
|
<tr class="row-even"><td><p>{osxphotos_cmd_line}</p></td>
|
||||||
<td><p>The full command line used to run osxphotos</p></td>
|
<td><p>The full command line used to run osxphotos</p></td>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="OSXPhotos Command Line Interface (CLI)" href="cli.html" /><link rel="prev" title="OSXPhotos" href="overview.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29"/>
|
||||||
<title>OSXPhotos Tutorial - osxphotos 0.59.0 documentation</title>
|
<title>OSXPhotos Tutorial - osxphotos 0.59.1 documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">osxphotos 0.59.0 documentation</div></a>
|
<a href="index.html"><div class="brand">osxphotos 0.59.1 documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
|
||||||
|
|
||||||
|
|
||||||
<span class="sidebar-brand-text">osxphotos 0.59.0 documentation</span>
|
<span class="sidebar-brand-text">osxphotos 0.59.1 documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ Template Substitutions
|
|||||||
* - {tab}
|
* - {tab}
|
||||||
- :A tab: '\t'
|
- :A tab: '\t'
|
||||||
* - {osxphotos_version}
|
* - {osxphotos_version}
|
||||||
- The osxphotos version, e.g. '0.59.0'
|
- The osxphotos version, e.g. '0.59.1'
|
||||||
* - {osxphotos_cmd_line}
|
* - {osxphotos_cmd_line}
|
||||||
- The full command line used to run osxphotos
|
- The full command line used to run osxphotos
|
||||||
* - {album}
|
* - {album}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.59.0"
|
__version__ = "0.59.1"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"""export command for osxphotos CLI"""
|
"""export command for osxphotos CLI"""
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
@@ -11,8 +9,7 @@ import shlex
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from typing import Iterable, List, Optional, Tuple, Any, Callable
|
from typing import Iterable, List, Optional, Tuple
|
||||||
import concurrent.futures
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from osxmetadata import (
|
from osxmetadata import (
|
||||||
@@ -1429,27 +1426,46 @@ def export(
|
|||||||
|
|
||||||
photo_num = 0
|
photo_num = 0
|
||||||
num_exported = 0
|
num_exported = 0
|
||||||
|
limit_str = f" (limit = [num]{limit}[/num])" if limit else ""
|
||||||
# hack to avoid passing all the options to export_photo
|
# hack to avoid passing all the options to export_photo
|
||||||
kwargs = locals().copy()
|
kwargs = {
|
||||||
|
k: v
|
||||||
|
for k, v in locals().items()
|
||||||
|
if k in inspect.getfullargspec(export_photo).args
|
||||||
|
}
|
||||||
kwargs["export_dir"] = dest
|
kwargs["export_dir"] = dest
|
||||||
kwargs["export_preview"] = preview
|
kwargs["export_preview"] = preview
|
||||||
limit_str = f" (limit = [num]{limit}[/num])" if limit else ""
|
|
||||||
with rich_progress(console=get_verbose_console(), mock=no_progress) as progress:
|
with rich_progress(console=get_verbose_console(), mock=no_progress) as progress:
|
||||||
task = progress.add_task(
|
task = progress.add_task(
|
||||||
f"Exporting [num]{num_photos}[/] photos{limit_str}", total=num_photos
|
f"Exporting [num]{num_photos}[/] photos{limit_str}", total=num_photos
|
||||||
)
|
)
|
||||||
futures = []
|
|
||||||
with concurrent.futures.ThreadPoolExecutor(
|
|
||||||
# max_workers=os.cpu_count()
|
|
||||||
max_workers=1,
|
|
||||||
) as executor:
|
|
||||||
for p in photos:
|
for p in photos:
|
||||||
photo_num += 1
|
photo_num += 1
|
||||||
kwargs["photo_num"] = photo_num
|
kwargs["photo"] = p
|
||||||
futures.append(executor.submit(export_worker, p, **kwargs))
|
export_results = export_photo(**kwargs)
|
||||||
|
if post_function:
|
||||||
|
for function in post_function:
|
||||||
|
# post function is tuple of (function, filename.py::function_name)
|
||||||
|
verbose(f"Calling post-function [bold]{function[1]}")
|
||||||
|
if not dry_run:
|
||||||
|
try:
|
||||||
|
function[0](p, export_results, verbose)
|
||||||
|
except Exception as e:
|
||||||
|
rich_echo_error(
|
||||||
|
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
|
run_post_command(
|
||||||
|
photo=p,
|
||||||
|
post_command=post_command,
|
||||||
|
export_results=export_results,
|
||||||
|
export_dir=dest,
|
||||||
|
dry_run=dry_run,
|
||||||
|
exiftool_path=exiftool_path,
|
||||||
|
export_db=export_db,
|
||||||
|
verbose=verbose,
|
||||||
|
)
|
||||||
|
|
||||||
for future in concurrent.futures.as_completed(futures):
|
|
||||||
p, export_results = future.result()
|
|
||||||
if album_export and export_results.exported:
|
if album_export and export_results.exported:
|
||||||
try:
|
try:
|
||||||
album_export.add(p)
|
album_export.add(p)
|
||||||
@@ -1508,9 +1524,7 @@ def export(
|
|||||||
if finder_tag_keywords or finder_tag_template:
|
if finder_tag_keywords or finder_tag_template:
|
||||||
if dry_run:
|
if dry_run:
|
||||||
for filepath in photo_files:
|
for filepath in photo_files:
|
||||||
verbose(
|
verbose(f"Writing Finder tags to [filepath]{filepath}[/]")
|
||||||
f"Writing Finder tags to [filepath]{filepath}[/]"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
tags_written, tags_skipped = write_finder_tags(
|
tags_written, tags_skipped = write_finder_tags(
|
||||||
p,
|
p,
|
||||||
@@ -1668,45 +1682,6 @@ def export(
|
|||||||
export_db.close()
|
export_db.close()
|
||||||
|
|
||||||
|
|
||||||
def export_worker(
|
|
||||||
photo: osxphotos.PhotoInfo, **kwargs
|
|
||||||
) -> tuple[osxphotos.PhotoInfo, ExportResults]:
|
|
||||||
"""Export worker function for multi-threaded export of photos"""
|
|
||||||
dry_run = kwargs["dry_run"]
|
|
||||||
verbose: Callable[[str], Any] = kwargs["verbose"]
|
|
||||||
export_args = {
|
|
||||||
k: v
|
|
||||||
for k, v in kwargs.items()
|
|
||||||
if k in inspect.getfullargspec(export_photo).args
|
|
||||||
}
|
|
||||||
export_args["photo"] = photo
|
|
||||||
export_results = export_photo(**export_args)
|
|
||||||
if post_function := kwargs["post_function"]:
|
|
||||||
for function in post_function:
|
|
||||||
# post function is tuple of (function, filename.py::function_name)
|
|
||||||
verbose(f"Calling post-function [bold]{function[1]}")
|
|
||||||
if not dry_run:
|
|
||||||
try:
|
|
||||||
function[0](photo, export_results, verbose)
|
|
||||||
except Exception as e:
|
|
||||||
rich_echo_error(
|
|
||||||
f"[error]Error running post-function [italic]{function[1]}[/italic]: {e}"
|
|
||||||
)
|
|
||||||
|
|
||||||
run_post_command(
|
|
||||||
photo=photo,
|
|
||||||
post_command=kwargs["post_command"],
|
|
||||||
export_results=export_results,
|
|
||||||
export_dir=kwargs["dest"],
|
|
||||||
dry_run=dry_run,
|
|
||||||
exiftool_path=kwargs["exiftool_path"],
|
|
||||||
export_db=kwargs["export_db"],
|
|
||||||
verbose=verbose,
|
|
||||||
)
|
|
||||||
|
|
||||||
return photo, export_results
|
|
||||||
|
|
||||||
|
|
||||||
def export_photo(
|
def export_photo(
|
||||||
photo=None,
|
photo=None,
|
||||||
dest=None,
|
dest=None,
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +1,11 @@
|
|||||||
""" Yet another simple exiftool wrapper
|
""" Yet another simple exiftool wrapper
|
||||||
I rolled my own for following reasons:
|
I rolled my own for following reasons:
|
||||||
1. I wanted something under MIT license (best alternative was licensed under GPL/BSD)
|
1. I wanted something under MIT license (best alternative was licensed under GPL/BSD)
|
||||||
2. I wanted exiftool processes to stay resident between calls (improved performance)
|
2. I wanted singleton behavior so only a single exiftool process was ever running
|
||||||
3. When used as a context manager, I wanted the operations to batch until exiting the context (improved performance)
|
3. When used as a context manager, I wanted the operations to batch until exiting the context (improved performance)
|
||||||
"""
|
If these aren't important to you, I highly recommend you use Sven Marnach's excellent
|
||||||
|
pyexiftool: https://github.com/smarnach/pyexiftool which provides more functionality """
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import contextlib
|
import contextlib
|
||||||
@@ -17,7 +17,6 @@ import pathlib
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from functools import lru_cache # pylint: disable=syntax-error
|
from functools import lru_cache # pylint: disable=syntax-error
|
||||||
|
|
||||||
@@ -31,8 +30,6 @@ __all__ = [
|
|||||||
"unescape_str",
|
"unescape_str",
|
||||||
]
|
]
|
||||||
|
|
||||||
logger = logging.getLogger("osxphotos")
|
|
||||||
|
|
||||||
# exiftool -stay_open commands outputs this EOF marker after command is run
|
# exiftool -stay_open commands outputs this EOF marker after command is run
|
||||||
EXIFTOOL_STAYOPEN_EOF = "{ready}"
|
EXIFTOOL_STAYOPEN_EOF = "{ready}"
|
||||||
EXIFTOOL_STAYOPEN_EOF_LEN = len(EXIFTOOL_STAYOPEN_EOF)
|
EXIFTOOL_STAYOPEN_EOF_LEN = len(EXIFTOOL_STAYOPEN_EOF)
|
||||||
@@ -45,8 +42,6 @@ EXIFTOOL_FILETYPES_JSON = "exiftool_filetypes.json"
|
|||||||
with (pathlib.Path(__file__).parent / EXIFTOOL_FILETYPES_JSON).open("r") as f:
|
with (pathlib.Path(__file__).parent / EXIFTOOL_FILETYPES_JSON).open("r") as f:
|
||||||
EXIFTOOL_SUPPORTED_FILETYPES = json.load(f)
|
EXIFTOOL_SUPPORTED_FILETYPES = json.load(f)
|
||||||
|
|
||||||
NUM_PROCESSES = os.cpu_count() or 1
|
|
||||||
|
|
||||||
|
|
||||||
def exiftool_can_write(suffix: str) -> bool:
|
def exiftool_can_write(suffix: str) -> bool:
|
||||||
"""Return True if exiftool supports writing to a file with the given suffix, otherwise False"""
|
"""Return True if exiftool supports writing to a file with the given suffix, otherwise False"""
|
||||||
@@ -101,11 +96,8 @@ def get_exiftool_path():
|
|||||||
|
|
||||||
|
|
||||||
class _ExifToolProc:
|
class _ExifToolProc:
|
||||||
"""
|
"""Runs exiftool in a subprocess via Popen
|
||||||
Runs exiftool in a subprocess via Popen
|
Creates a singleton object"""
|
||||||
Creates a singleton object that dispatches commands to one or
|
|
||||||
more exiftool subprocesses.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
"""create new object or return instance of already created singleton"""
|
"""create new object or return instance of already created singleton"""
|
||||||
@@ -114,11 +106,7 @@ class _ExifToolProc:
|
|||||||
|
|
||||||
return cls.instance
|
return cls.instance
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, exiftool=None, large_file_support=True):
|
||||||
self,
|
|
||||||
exiftool: str | None = None,
|
|
||||||
large_file_support: bool = True,
|
|
||||||
):
|
|
||||||
"""construct _ExifToolProc singleton object or return instance of already created object
|
"""construct _ExifToolProc singleton object or return instance of already created object
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -129,7 +117,7 @@ class _ExifToolProc:
|
|||||||
if hasattr(self, "_process_running") and self._process_running:
|
if hasattr(self, "_process_running") and self._process_running:
|
||||||
# already running
|
# already running
|
||||||
if exiftool is not None and exiftool != self._exiftool:
|
if exiftool is not None and exiftool != self._exiftool:
|
||||||
logger.warning(
|
logging.warning(
|
||||||
f"exiftool subprocess already running, "
|
f"exiftool subprocess already running, "
|
||||||
f"ignoring exiftool={exiftool}"
|
f"ignoring exiftool={exiftool}"
|
||||||
)
|
)
|
||||||
@@ -137,9 +125,6 @@ class _ExifToolProc:
|
|||||||
self._process_running = False
|
self._process_running = False
|
||||||
self._large_file_support = large_file_support
|
self._large_file_support = large_file_support
|
||||||
self._exiftool = exiftool or get_exiftool_path()
|
self._exiftool = exiftool or get_exiftool_path()
|
||||||
self._num_processes = NUM_PROCESSES
|
|
||||||
self._process = []
|
|
||||||
self._process_counter = 0
|
|
||||||
self._start_proc(large_file_support=large_file_support)
|
self._start_proc(large_file_support=large_file_support)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -147,20 +132,23 @@ class _ExifToolProc:
|
|||||||
"""return the exiftool subprocess"""
|
"""return the exiftool subprocess"""
|
||||||
if not self._process_running:
|
if not self._process_running:
|
||||||
self._start_proc(large_file_support=self._large_file_support)
|
self._start_proc(large_file_support=self._large_file_support)
|
||||||
process_idx = self._process_counter % self._num_processes
|
return self._process
|
||||||
self._process_counter += 1
|
|
||||||
return self._process[process_idx]
|
@property
|
||||||
|
def pid(self):
|
||||||
|
"""return process id (PID) of the exiftool process"""
|
||||||
|
return self._process.pid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def exiftool(self):
|
def exiftool(self):
|
||||||
"""return path to exiftool process"""
|
"""return path to exiftool process"""
|
||||||
return self._exiftool
|
return self._exiftool
|
||||||
|
|
||||||
def _start_proc(self, large_file_support: bool):
|
def _start_proc(self, large_file_support):
|
||||||
"""start exiftool in batch mode"""
|
"""start exiftool in batch mode"""
|
||||||
|
|
||||||
if self._process_running:
|
if self._process_running:
|
||||||
logger.debug(f"exiftool already running: {self._process}")
|
logging.warning("exiftool already running: {self._process}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# open exiftool process
|
# open exiftool process
|
||||||
@@ -168,9 +156,7 @@ class _ExifToolProc:
|
|||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["PATH"] = f'/usr/bin/:{env["PATH"]}'
|
env["PATH"] = f'/usr/bin/:{env["PATH"]}'
|
||||||
large_file_args = ["-api", "largefilesupport=1"] if large_file_support else []
|
large_file_args = ["-api", "largefilesupport=1"] if large_file_support else []
|
||||||
for _ in range(self._num_processes):
|
self._process = subprocess.Popen(
|
||||||
self._process.append(
|
|
||||||
subprocess.Popen(
|
|
||||||
[
|
[
|
||||||
self._exiftool,
|
self._exiftool,
|
||||||
"-stay_open", # keep process open in batch mode
|
"-stay_open", # keep process open in batch mode
|
||||||
@@ -189,7 +175,6 @@ class _ExifToolProc:
|
|||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
self._process_running = True
|
self._process_running = True
|
||||||
|
|
||||||
EXIFTOOL_PROCESSES.append(self)
|
EXIFTOOL_PROCESSES.append(self)
|
||||||
@@ -200,19 +185,17 @@ class _ExifToolProc:
|
|||||||
if not self._process_running:
|
if not self._process_running:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(self._num_processes):
|
|
||||||
process = self._process[i]
|
|
||||||
with contextlib.suppress(Exception):
|
with contextlib.suppress(Exception):
|
||||||
process.stdin.write(b"-stay_open\n")
|
self._process.stdin.write(b"-stay_open\n")
|
||||||
process.stdin.write(b"False\n")
|
self._process.stdin.write(b"False\n")
|
||||||
process.stdin.flush()
|
self._process.stdin.flush()
|
||||||
try:
|
try:
|
||||||
process.communicate(timeout=5)
|
self._process.communicate(timeout=5)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
process.kill()
|
self._process.kill()
|
||||||
process.communicate()
|
self._process.communicate()
|
||||||
|
|
||||||
self._process = []
|
del self._process
|
||||||
self._process_running = False
|
self._process_running = False
|
||||||
|
|
||||||
|
|
||||||
@@ -250,7 +233,6 @@ class ExifTool:
|
|||||||
self._exiftoolproc = _ExifToolProc(
|
self._exiftoolproc = _ExifToolProc(
|
||||||
exiftool=exiftool, large_file_support=large_file_support
|
exiftool=exiftool, large_file_support=large_file_support
|
||||||
)
|
)
|
||||||
self._lock = threading.Lock()
|
|
||||||
self._read_exif()
|
self._read_exif()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -354,7 +336,6 @@ class ExifTool:
|
|||||||
if not commands:
|
if not commands:
|
||||||
raise TypeError("must provide one or more command to run")
|
raise TypeError("must provide one or more command to run")
|
||||||
|
|
||||||
with self._lock:
|
|
||||||
if self._context_mgr and self.overwrite:
|
if self._context_mgr and self.overwrite:
|
||||||
commands = list(commands)
|
commands = list(commands)
|
||||||
commands.append("-overwrite_original")
|
commands.append("-overwrite_original")
|
||||||
@@ -380,16 +361,15 @@ class ExifTool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# send the command
|
# send the command
|
||||||
process = self._process
|
self._process.stdin.write(command_str)
|
||||||
process.stdin.write(command_str)
|
self._process.stdin.flush()
|
||||||
process.stdin.flush()
|
|
||||||
|
|
||||||
# read the output
|
# read the output
|
||||||
output = b""
|
output = b""
|
||||||
warning = b""
|
warning = b""
|
||||||
error = b""
|
error = b""
|
||||||
while EXIFTOOL_STAYOPEN_EOF not in str(output):
|
while EXIFTOOL_STAYOPEN_EOF not in str(output):
|
||||||
line = process.stdout.readline()
|
line = self._process.stdout.readline()
|
||||||
if line.startswith(b"Warning"):
|
if line.startswith(b"Warning"):
|
||||||
warning += line.strip()
|
warning += line.strip()
|
||||||
elif line.startswith(b"Error"):
|
elif line.startswith(b"Error"):
|
||||||
@@ -403,6 +383,11 @@ class ExifTool:
|
|||||||
|
|
||||||
return output[:-EXIFTOOL_STAYOPEN_EOF_LEN], warning, error
|
return output[:-EXIFTOOL_STAYOPEN_EOF_LEN], warning, error
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pid(self):
|
||||||
|
"""return process id (PID) of the exiftool process"""
|
||||||
|
return self._process.pid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
"""returns exiftool version"""
|
"""returns exiftool version"""
|
||||||
@@ -419,7 +404,7 @@ class ExifTool:
|
|||||||
"""
|
"""
|
||||||
json_str, _, _ = self.run_commands("-json")
|
json_str, _, _ = self.run_commands("-json")
|
||||||
if not json_str:
|
if not json_str:
|
||||||
return {}
|
return dict()
|
||||||
json_str = unescape_str(json_str.decode("utf-8"))
|
json_str = unescape_str(json_str.decode("utf-8"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -427,8 +412,8 @@ class ExifTool:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
# will fail with some commands, e.g --ext AVI which produces
|
# will fail with some commands, e.g --ext AVI which produces
|
||||||
# 'No file with specified extension' instead of json
|
# 'No file with specified extension' instead of json
|
||||||
logger.warning(f"error loading json returned by exiftool: {e} {json_str}")
|
logging.warning(f"error loading json returned by exiftool: {e} {json_str}")
|
||||||
return {}
|
return dict()
|
||||||
exifdict = exifdict[0]
|
exifdict = exifdict[0]
|
||||||
if not tag_groups:
|
if not tag_groups:
|
||||||
# strip tag groups
|
# strip tag groups
|
||||||
@@ -497,12 +482,7 @@ class _ExifToolCaching(ExifTool):
|
|||||||
"""
|
"""
|
||||||
self._json_cache = None
|
self._json_cache = None
|
||||||
self._asdict_cache = {}
|
self._asdict_cache = {}
|
||||||
super().__init__(
|
super().__init__(filepath, exiftool=exiftool, overwrite=False, flags=None)
|
||||||
filepath,
|
|
||||||
exiftool=exiftool,
|
|
||||||
overwrite=False,
|
|
||||||
flags=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def run_commands(self, *commands, no_file=False):
|
def run_commands(self, *commands, no_file=False):
|
||||||
if commands[0] not in ["-json", "-ver"]:
|
if commands[0] not in ["-json", "-ver"]:
|
||||||
|
|||||||
@@ -1153,7 +1153,7 @@ class PhotoTemplate:
|
|||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise SyntaxError(
|
raise SyntaxError(
|
||||||
f"comparison operators may only be used with values that can be converted to numbers: {vals} {conditional_value}"
|
f"comparison operators may only be used with values that can be converted to numbers: {value} {conditional_value}"
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
predicate_is_true = False
|
predicate_is_true = False
|
||||||
@@ -1682,7 +1682,7 @@ def format_date_field(dt: datetime.datetime, field: str, args: List[str]) -> str
|
|||||||
raise ValueError(f"Unhandled template value: {field}") from e
|
raise ValueError(f"Unhandled template value: {field}") from e
|
||||||
|
|
||||||
|
|
||||||
def get_place_value(photo: "PhotoInfo", field: str):
|
def get_place_value(photo: "PhotoInfo", field: str): # noqa: F821
|
||||||
"""Get the value of a 'place' field by attribute
|
"""Get the value of a 'place' field by attribute
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -432,7 +432,9 @@ def lock_filename(filepath: Union[str, pathlib.Path]) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
filepath if lock file created, False if lock file already exists
|
filepath if lock file created, False if lock file already exists
|
||||||
"""
|
"""
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
# TODO: for future implementation
|
||||||
lockfile = pathlib.Path(f"{filepath}.osxphotos.lock")
|
lockfile = pathlib.Path(f"{filepath}.osxphotos.lock")
|
||||||
if lockfile.exists():
|
if lockfile.exists():
|
||||||
return False
|
return False
|
||||||
@@ -447,6 +449,9 @@ def unlock_filename(filepath: Union[str, pathlib.Path]):
|
|||||||
filepath: str or pathlib.Path; full path, including file name
|
filepath: str or pathlib.Path; full path, including file name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: for future implementation
|
||||||
lockfile = pathlib.Path(f"{filepath}.osxphotos.lock")
|
lockfile = pathlib.Path(f"{filepath}.osxphotos.lock")
|
||||||
if lockfile.exists():
|
if lockfile.exists():
|
||||||
lockfile.unlink()
|
lockfile.unlink()
|
||||||
@@ -539,6 +544,7 @@ def shortuuid_to_uuid(short_uuid: str) -> str:
|
|||||||
"""Convert shortuuid to uuid"""
|
"""Convert shortuuid to uuid"""
|
||||||
return str(shortuuid.decode(short_uuid)).upper()
|
return str(shortuuid.decode(short_uuid)).upper()
|
||||||
|
|
||||||
|
|
||||||
def under_test() -> bool:
|
def under_test() -> bool:
|
||||||
"""Return True if running under pytest"""
|
"""Return True if running under pytest"""
|
||||||
return "pytest" in sys.modules
|
return "pytest" in sys.modules
|
||||||
@@ -12,8 +12,8 @@ UUID_MISSING = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# photos with matching names
|
# photos with matching names
|
||||||
QUERY_NAME = "AAF035"
|
QUERY_NAME = "IMG_"
|
||||||
QUERY_COUNT = 4
|
QUERY_COUNT = 6
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.addalbum
|
@pytest.mark.addalbum
|
||||||
|
|||||||
@@ -419,6 +419,22 @@ def test_addvalues_unicode():
|
|||||||
assert sorted(exif.data["IPTC:Keywords"]) == sorted(["ǂ", "Ƕ"])
|
assert sorted(exif.data["IPTC:Keywords"]) == sorted(["ǂ", "Ƕ"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_singleton():
|
||||||
|
import osxphotos.exiftool
|
||||||
|
|
||||||
|
exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD)
|
||||||
|
exif2 = osxphotos.exiftool.ExifTool(TEST_FILE_MULTI_KEYWORD)
|
||||||
|
|
||||||
|
assert exif1._process.pid == exif2._process.pid
|
||||||
|
|
||||||
|
|
||||||
|
def test_pid():
|
||||||
|
import osxphotos.exiftool
|
||||||
|
|
||||||
|
exif1 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD)
|
||||||
|
assert exif1.pid == exif1._process.pid
|
||||||
|
|
||||||
|
|
||||||
def test_exiftoolproc_process():
|
def test_exiftoolproc_process():
|
||||||
import osxphotos.exiftool
|
import osxphotos.exiftool
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ def test_dd_to_dms():
|
|||||||
|
|
||||||
@pytest.mark.skip(reason="Fails on some machines")
|
@pytest.mark.skip(reason="Fails on some machines")
|
||||||
def test_get_system_library_path():
|
def test_get_system_library_path():
|
||||||
|
|
||||||
_, major, _ = osxphotos.utils._get_os_version()
|
_, major, _ = osxphotos.utils._get_os_version()
|
||||||
if int(major) < 15:
|
if int(major) < 15:
|
||||||
assert osxphotos.utils.get_system_library_path() is None
|
assert osxphotos.utils.get_system_library_path() is None
|
||||||
@@ -84,7 +83,6 @@ def test_list_directory():
|
|||||||
|
|
||||||
|
|
||||||
def test_list_directory_invalid():
|
def test_list_directory_invalid():
|
||||||
|
|
||||||
temp_dir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
temp_dir = tempfile.TemporaryDirectory(prefix="osxphotos_")
|
||||||
files = list_directory(f"{temp_dir.name}/no_such_dir", glob="*.jpg")
|
files = list_directory(f"{temp_dir.name}/no_such_dir", glob="*.jpg")
|
||||||
assert len(files) == 0
|
assert len(files) == 0
|
||||||
@@ -151,6 +149,7 @@ def test_increment_filename_with_lock():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="Lock files not yet implemented")
|
||||||
def test_increment_filename_with_lock_exists():
|
def test_increment_filename_with_lock_exists():
|
||||||
# test that increment_filename works with lock=True when lock file already exists
|
# test that increment_filename works with lock=True when lock file already exists
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user