summaryrefslogtreecommitdiff
path: root/rapid
diff options
context:
space:
mode:
Diffstat (limited to 'rapid')
-rw-r--r--rapid/ChangeLog59
-rw-r--r--rapid/INSTALL18
-rw-r--r--rapid/TODO26
-rw-r--r--rapid/config.py12
-rw-r--r--rapid/glade3/filmstrip-100x75.xpm106
-rw-r--r--rapid/glade3/rapid.glade689
-rw-r--r--rapid/glade3/video.svg1638
-rwxr-xr-xrapid/media.py157
-rwxr-xr-xrapid/metadata.py173
-rw-r--r--rapid/prefs.py10
-rwxr-xr-xrapid/rapid.py1761
-rw-r--r--rapid/renamesubfolderprefs.py283
-rw-r--r--rapid/renamesubfolderprefstest.py63
-rwxr-xr-xrapid/videometadata.py187
14 files changed, 4360 insertions, 822 deletions
diff --git a/rapid/ChangeLog b/rapid/ChangeLog
index 0c2d27a..325b386 100644
--- a/rapid/ChangeLog
+++ b/rapid/ChangeLog
@@ -1,10 +1,65 @@
+Version 0.2.0
+-------------
+
+2010-05-30
+
+Videos can now be downloaded in much the same way photos can.
+
+The package kaa metadata is required to download videos. ffmpegthumbnailer is
+used to display thumbnail images of certain types of videos as the download
+occurs.
+
+kaa metadata and ffmpegthumbnailer are optional. The program will run without
+them. See the INSTALL file for details.
+
+If a THM file with the same name as the video is present, it will be used to
+generate a thumbnail for the video. If not, if ffmpegthumbnailer is installed,
+Rapid Photo Downloader will use it to attempt to extract a thumbnail from the
+video. THM files are not downloaded.
+
+For now, sequence values are shared between the downloads of videos and photos.
+There may be an option to have two sets of sequence numbers in a future release.
+
+Due to the number of changes in the code, it is possible that regressions in the
+photo downloading code may have been introduced.
+
+This is the first release to use version 0.2.x of the pyexiv2 library. The
+most immediate benefit of this change is that thumbnail images from Nikon and
+other brand cameras can be displayed. This fixes bugs #369640 and #570378.
+
+Please note pyexiv2 0.2.x requires exiv2 0.1.9 or above.
+
+Rapid Photo Downloader will still work with pyexiv2 0.1.x. However it will not
+be able to display the thumbnails of some brands of camera.
+
+If Rapid Photo Downloader detects version 0.18.1 or higher of the exiv2
+library, it will download Panasonic's RW2 files. If it detects version 0.18.0 or
+higher of the exiv2 library, it will download Mamiya's MEF files. For Rapid
+Photo Downloader to be able to detect which version of the exiv2 library your
+system has, it must either be running pyexiv2 >= 0.2.0, or have exiv2 installed.
+
+Fixed bug #483222: sometimes images could not be downloaded to NTFS partitions.
+This fix was a welcome side effect of using GIO to copy images, instead of
+relying on the python standard libary.
+
+Error message headings in the Error Log are now displayed in a red font.
+
+Program settings and preferences can be reset using a new command line option.
+
+Program preferences are now more thoroughly checked for validity when the
+program starts.
+
+Further work was done to fix bug #505492, to handle cases where the system
+notification system is not working properly.
+
+
Version 0.1.3
-------------
2010-01-22
Fixed bug #509348: When both the backup and "Delete images from image device
-upon download completion" options are selected, the program should only delete
+upon download completion" options are selected, the program will only delete
an image from the image device if it was both downloaded to the download folder
and backed up. Previously it did not check to ensure it was backed up
correctly too.
@@ -23,7 +78,7 @@ is writeable. If automatic detection of image devices is not enabled, it checks
to see if the image location path exists.
Updated Czech, Dutch, Finnish, French, German, Hungarian, Italian, Polish,
-Russian, Serbian, Spanish and Swedish translations
+Russian, Serbian, Spanish and Swedish translations.
Version 0.1.2
diff --git a/rapid/INSTALL b/rapid/INSTALL
index 7972a01..359b265 100644
--- a/rapid/INSTALL
+++ b/rapid/INSTALL
@@ -10,8 +10,20 @@ Rapid Photo Downloader depends on the following software:
- libexiv2 0.15 or higher
- pyexiv2 0.1.1 or higher
-To run Rapid Photo Downloader you will need all the software mentioned above. To
-start from a fairly basic system, I suggest the following:
+
+To run Rapid Photo Downloader you will need all the software mentioned above. If
+you want to download videos, you can install:
+
+- python-kaa-metadata
+- ffmpegthumbnailer
+
+kaa metadata is required to download videos. ffmpegthumbnailer is used only to
+display thumbnail images as the download occurs.
+
+kaa metadata and ffmpegthumbnailer are optional. The program will run without
+them.
+
+To start from a fairly basic system, I suggest the following:
1. Install the required gnome packages from your linux distribution's package
repositories. On a recent linux distribution, expect all to be available for
@@ -24,7 +36,7 @@ start from a fairly basic system, I suggest the following:
repositories (look for python-pyexiv2). But if not, there are instructions
here:
- http://tilloy.net/dev/pyexiv2/developers.htm
+ http://tilloy.net/dev/pyexiv2/download.html
3. Install this application from the tarball. You probably need to be the
super user (root) to be able to do this:
diff --git a/rapid/TODO b/rapid/TODO
deleted file mode 100644
index 5465850..0000000
--- a/rapid/TODO
+++ /dev/null
@@ -1,26 +0,0 @@
-For major release (0.10.0):
-===========================
-
-* sequencing:
- - allow generation of unique sequences based on date time for last 1 - n days, via menu
-
-* download profiles (combinations of subfolder and image renaming preferences)
-* better handle preference changes while downloads are occurring
-* handle unexpected media removal, disk full, all kinds of write errors
-* indicate different levels of error better in log window
-* sum amount of images to be downloaded, and how much free space on the file system
-* check download locations and sources still valid when starting the application
-* rework checkPrefsForValidity() to account for multiple subfolder problems
-
-For future releases:
-====================
-
-* Better application icon -- help needed!
-* Get more metadata
- - lens
-* User defined camera model mapping
-* Job codes
-* use python's high performance deque to improve idletube
-
-
-
diff --git a/rapid/config.py b/rapid/config.py
index d9f4b1f..1ece2b0 100644
--- a/rapid/config.py
+++ b/rapid/config.py
@@ -15,7 +15,7 @@
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-version = '0.1.3'
+version = '0.2.0'
GCONF_KEY="/apps/rapid-photo-downloader"
GLADE_FILE = "glade3/rapid.glade"
@@ -36,15 +36,11 @@ IGNORE = "ignore"
DEFAULT_PHOTO_LOCATIONS = ['Pictures', 'Photos']
DEFAULT_BACKUP_LOCATION = 'Pictures'
+DEFAULT_VIDEO_BACKUP_LOCATION = 'Videos'
-MAX_NO_READERS = 20
-
-RAW_FILE_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mef', 'mos', 'mrw',
- 'nef', 'orf', 'pef', 'raf', 'raw', 'sr2']
+DEFAULT_VIDEO_LOCATIONS = ['Videos']
-#exiv2 0.18.1 introduces support for Panasonic .RW2 files
-
-NON_RAW_IMAGE_FILE_EXTENSIONS = ['jpg', 'jpe', 'jpeg', 'tif', 'tiff']
+MAX_NO_READERS = 20
CRITICAL_ERROR = 1
SERIOUS_ERROR = 2
diff --git a/rapid/glade3/filmstrip-100x75.xpm b/rapid/glade3/filmstrip-100x75.xpm
new file mode 100644
index 0000000..5e3428e
--- /dev/null
+++ b/rapid/glade3/filmstrip-100x75.xpm
@@ -0,0 +1,106 @@
+/* XPM */
+static char * thumbnail_100x75_xpm[] = {
+"100 75 28 1",
+" c None",
+". c #000000",
+"+ c #232323",
+"@ c #7A7A7A",
+"# c #838383",
+"$ c #8C8C8C",
+"% c #909090",
+"& c #8E8E8E",
+"* c #525252",
+"= c #6E6E6E",
+"- c #939393",
+"; c #A3A3A3",
+"> c #ABABAB",
+", c #A8A8A8",
+"' c #9B9B9B",
+") c #727272",
+"! c #A4A4A4",
+"~ c #BBBBBB",
+"{ c #C4C4C4",
+"] c #C1C1C1",
+"^ c #AFAFAF",
+"/ c #3E3E3E",
+"( c #A6A6A6",
+"_ c #BEBEBE",
+": c #C8C8C8",
+"< c #070707",
+"[ c #090909",
+"} c #0A0A0A",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+....",
+"....=#-;>,'. .',>;-#=....",
+"....)&!~{]^. .^]{~!&)....",
+"..../%(_:{&. .&{:_(%/....",
+".....<[}}}.. ..}}}[<.....",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"............ ............",
+"....+@#$%&*. .*&%$#@+...."};
diff --git a/rapid/glade3/rapid.glade b/rapid/glade3/rapid.glade
index f379024..48a32a8 100644
--- a/rapid/glade3/rapid.glade
+++ b/rapid/glade3/rapid.glade
@@ -65,7 +65,7 @@
<child>
<widget class="GtkImage" id="image2">
<property name="visible">True</property>
- <property name="stock">gtk-open</property>
+ <property name="stock">gtk-directory</property>
</widget>
<packing>
<property name="expand">False</property>
@@ -75,7 +75,7 @@
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
- <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Download Folder&lt;/span&gt;</property>
+ <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Photo Download Folders&lt;/span&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
@@ -124,26 +124,12 @@
<property name="n_rows">7</property>
<property name="n_columns">3</property>
<child>
- <widget class="GtkVBox" id="subfolder_vbox">
- <property name="visible">True</property>
- <child>
- <placeholder/>
- </child>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">3</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- <property name="y_padding">12</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="example_download_path_label">
+ <widget class="GtkLabel" id="example_photo_download_path_label">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;i&gt;Example: /home/user/photos&lt;/i&gt;</property>
+ <property name="label" translatable="yes">&lt;i&gt;Example: /home/user/Pictures&lt;/i&gt;</property>
<property name="use_markup">True</property>
+ <property name="wrap">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
@@ -211,7 +197,7 @@
</packing>
</child>
<child>
- <widget class="GtkLabel" id="subfolder_warning_label">
+ <widget class="GtkLabel" id="photo_subfolder_warning_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
@@ -227,6 +213,21 @@
</packing>
</child>
<child>
+ <widget class="GtkVBox" id="subfolder_vbox">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_padding">12</property>
+ </packing>
+ </child>
+ <child>
<placeholder/>
</child>
<child>
@@ -260,7 +261,6 @@
</child>
</widget>
<packing>
- <property name="expand">False</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -273,7 +273,7 @@
<child>
<widget class="GtkLabel" id="download_label">
<property name="visible">True</property>
- <property name="label" translatable="yes">Download Folder</property>
+ <property name="label" translatable="yes">Photo Folders</property>
</widget>
<packing>
<property name="tab_fill">False</property>
@@ -304,7 +304,7 @@
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
- <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Image Rename&lt;/span&gt; </property>
+ <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Photo Rename&lt;/span&gt; </property>
<property name="use_markup">True</property>
</widget>
<packing>
@@ -353,7 +353,7 @@
<child>
<widget class="GtkLabel" id="label42">
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;Image Rename&lt;/b&gt;</property>
+ <property name="label" translatable="yes">&lt;b&gt;Photo Rename&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
@@ -527,7 +527,7 @@
<child>
<widget class="GtkLabel" id="rename_label">
<property name="visible">True</property>
- <property name="label" translatable="yes">Image Rename</property>
+ <property name="label" translatable="yes">Photo Rename</property>
</widget>
<packing>
<property name="position">1</property>
@@ -536,6 +536,514 @@
</packing>
</child>
<child>
+ <widget class="GtkVBox" id="folder_tab1">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-directory</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label46">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Video Download Folders&lt;/span&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator8">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="folder_videos_cannot_be_downloaded_hbox">
+ <child>
+ <widget class="GtkLabel" id="folder_videos_cannot_be_downloaded_label">
+ <property name="xalign">0</property>
+ <property name="xpad">12</property>
+ <property name="ypad">10</property>
+ <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the &lt;i&gt;kaa metadata&lt;/i&gt; package for python.</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="video_folders_hbox">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkLabel" id="label57">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="video_download_folder_table">
+ <property name="visible">True</property>
+ <property name="n_rows">7</property>
+ <property name="n_columns">3</property>
+ <child>
+ <widget class="GtkLabel" id="lblPhotos2">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Download folder:</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label59">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">12</property>
+ <property name="label" translatable="yes">Choose the download folder. Subfolders for the downloaded videos will be automatically created in this folder using the structure specified below.</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Download Folder&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Download Subfolders&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="example_video_download_path_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;i&gt;Example: /home/user/Pictures&lt;/i&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="video_subfolder_warning_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">word-char</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="video_subfolder_vbox">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_padding">12</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">12</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="video_download_folder">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Video Folders</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="video_rename_tab">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-convert</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Video Rename&lt;/span&gt; </property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="rename_vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkLabel" id="videos_cannot_be_downloaded_label">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the &lt;i&gt;kaa metadata&lt;/i&gt; package for python.</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="video_rename_scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="window_placement_set">True</property>
+ <child>
+ <widget class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <widget class="GtkVBox" id="video_rename_table_vbox">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="video_rename_example_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <child>
+ <widget class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label56">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"> </property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="video_new_name_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label">translators please ignore this</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="video_original_name_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label">translators please ignore this</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="new_video_filename_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">&lt;i&gt;New:&lt;/i&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="original_video_filename_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;i&gt;Original:&lt;/i&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="example_video_filename_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Example&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="y_padding">12</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">12</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="video_rename_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Video Rename</property>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ <child>
<widget class="GtkVBox" id="rename_options_tab">
<property name="visible">True</property>
<property name="spacing">12</property>
@@ -844,7 +1352,7 @@
<widget class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Specify whether image and folder names should have any characters removed that are not allowed by other operating systems.</property>
+ <property name="label" translatable="yes">Specify whether photo, video and folder names should have any characters removed that are not allowed by other operating systems.</property>
<property name="wrap">True</property>
</widget>
<packing>
@@ -896,7 +1404,7 @@
</child>
</widget>
<packing>
- <property name="position">2</property>
+ <property name="position">4</property>
</packing>
</child>
<child>
@@ -905,7 +1413,7 @@
<property name="label" translatable="yes">Rename Options</property>
</widget>
<packing>
- <property name="position">2</property>
+ <property name="position">4</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
@@ -969,7 +1477,6 @@
<child>
<widget class="GtkVBox" id="job_code_vbox">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<child>
<widget class="GtkLabel" id="job_code_label">
<property name="xalign">0</property>
@@ -1101,7 +1608,7 @@
</child>
</widget>
<packing>
- <property name="position">2</property>
+ <property name="position">5</property>
</packing>
</child>
<child>
@@ -1110,7 +1617,7 @@
<property name="label" translatable="yes">Job Codes</property>
</widget>
<packing>
- <property name="position">3</property>
+ <property name="position">5</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
@@ -1139,7 +1646,7 @@
<child>
<widget class="GtkLabel" id="label22">
<property name="visible">True</property>
- <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Image Devices&lt;/span&gt;</property>
+ <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Devices&lt;/span&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
@@ -1176,7 +1683,7 @@
<widget class="GtkLabel" id="label41">
<property name="xalign">0</property>
<property name="xpad">12</property>
- <property name="label" translatable="yes">&lt;b&gt;Image Devices&lt;/b&gt;</property>
+ <property name="label" translatable="yes">&lt;b&gt;Devices&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
@@ -1189,9 +1696,9 @@
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
- <property name="label" translatable="yes">Image devices are devices from which to download photos, such as cameras, memory cards or Portable Storage Devices.
+ <property name="label" translatable="yes">Devices are from where to download photos and videos, such as cameras, memory cards or Portable Storage Devices.
-You can download photos from multiple image devices simultaneously.
+You can download photos from multiple devices simultaneously, or you can specify a location on your hard drive.
&lt;i&gt;If downloading directly from your camera works poorly or not at all, try setting it to PTP mode. If that is not possible, consider using a card reader.&lt;/i&gt;</property>
<property name="use_markup">True</property>
@@ -1238,7 +1745,7 @@ You can download photos from multiple image devices simultaneously.
</child>
<child>
<widget class="GtkCheckButton" id="autodetect_device_checkbutton">
- <property name="label" translatable="yes">Automatically detect image devices</property>
+ <property name="label" translatable="yes">Automatically detect devices</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -1305,7 +1812,7 @@ You can download photos from multiple image devices simultaneously.
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
- <property name="label" translatable="yes">Image location:</property>
+ <property name="label" translatable="yes">Location:</property>
</widget>
<packing>
<property name="top_attach">1</property>
@@ -1321,7 +1828,7 @@ You can download photos from multiple image devices simultaneously.
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="ypad">12</property>
- <property name="label" translatable="yes">If you disable automatic detection, choose the exact location of the images.</property>
+ <property name="label" translatable="yes">If you disable automatic detection, choose the exact location of the images and videos.</property>
<property name="wrap">True</property>
</widget>
<packing>
@@ -1363,16 +1870,16 @@ You can download photos from multiple image devices simultaneously.
</child>
</widget>
<packing>
- <property name="position">4</property>
+ <property name="position">6</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="device_label">
<property name="visible">True</property>
- <property name="label" translatable="yes">Image Devices</property>
+ <property name="label" translatable="yes">Devices</property>
</widget>
<packing>
- <property name="position">4</property>
+ <property name="position">6</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
@@ -1460,7 +1967,7 @@ You can download photos from multiple image devices simultaneously.
<child>
<widget class="GtkTable" id="backup_table">
<property name="visible">True</property>
- <property name="n_rows">8</property>
+ <property name="n_rows">9</property>
<property name="n_columns">4</property>
<child>
<widget class="GtkLabel" id="backup_location_explanation_label">
@@ -1474,8 +1981,8 @@ You can download photos from multiple image devices simultaneously.
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
- <property name="top_attach">6</property>
- <property name="bottom_attach">7</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
</packing>
</child>
<child>
@@ -1501,7 +2008,7 @@ You can download photos from multiple image devices simultaneously.
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
- <property name="label" translatable="yes">You can have your photos backed up to multiple locations as they are downloaded, e.g. external hard drives.</property>
+ <property name="label" translatable="yes">You can have your photos and videos backed up to multiple locations as they are downloaded, e.g. external hard drives.</property>
<property name="wrap">True</property>
</widget>
<packing>
@@ -1511,7 +2018,7 @@ You can download photos from multiple image devices simultaneously.
</child>
<child>
<widget class="GtkCheckButton" id="backup_checkbutton">
- <property name="label" translatable="yes">Backup photos when downloading</property>
+ <property name="label" translatable="yes">Backup photos and videos when downloading</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -1533,7 +2040,7 @@ You can download photos from multiple image devices simultaneously.
<property name="ypad">6</property>
<property name="label" translatable="yes">Specify the folder in which backups are stored on the device.
-&lt;i&gt;Note: this will also be used to determine whether or not the device is used for backups. For each device you wish to use for backing up to, create a folder in it with this name.&lt;/i&gt;</property>
+&lt;i&gt;Note: this will also be used to determine whether or not the device is used for backups. For each device you wish to use for backing up to, create a folder in it with one of these names.&lt;/i&gt;</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</widget>
@@ -1553,8 +2060,8 @@ You can download photos from multiple image devices simultaneously.
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
- <property name="top_attach">7</property>
- <property name="bottom_attach">8</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@@ -1563,7 +2070,7 @@ You can download photos from multiple image devices simultaneously.
<widget class="GtkLabel" id="backup_identifier_label">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Backup folder name:</property>
+ <property name="label" translatable="yes">Photo backup folder name:</property>
</widget>
<packing>
<property name="left_attach">2</property>
@@ -1572,6 +2079,7 @@ You can download photos from multiple image devices simultaneously.
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
+ <property name="y_padding">6</property>
</packing>
</child>
<child>
@@ -1586,8 +2094,8 @@ You can download photos from multiple image devices simultaneously.
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
@@ -1603,8 +2111,8 @@ You can download photos from multiple image devices simultaneously.
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
</packing>
</child>
<child>
@@ -1620,10 +2128,49 @@ You can download photos from multiple image devices simultaneously.
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
- <property name="y_padding">12</property>
+ <property name="y_padding">6</property>
</packing>
</child>
<child>
+ <widget class="GtkLabel" id="video_backup_identifier_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Video backup folder name:</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ <property name="y_padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="video_backup_identifier_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <signal name="changed" handler="on_video_backup_identifier_entry_changed"/>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="y_options"></property>
+ <property name="y_padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
<placeholder/>
</child>
<child>
@@ -1681,7 +2228,7 @@ You can download photos from multiple image devices simultaneously.
</child>
</widget>
<packing>
- <property name="position">5</property>
+ <property name="position">7</property>
</packing>
</child>
<child>
@@ -1690,7 +2237,7 @@ You can download photos from multiple image devices simultaneously.
<property name="label" translatable="yes">Backup</property>
</widget>
<packing>
- <property name="position">5</property>
+ <property name="position">7</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
@@ -1780,7 +2327,7 @@ You can download photos from multiple image devices simultaneously.
<property name="n_columns">3</property>
<child>
<widget class="GtkCheckButton" id="auto_unmount_checkbutton">
- <property name="label" translatable="yes">Unmount ("eject") image device upon download completion</property>
+ <property name="label" translatable="yes">Unmount ("eject") device upon download completion</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -1810,7 +2357,7 @@ You can download photos from multiple image devices simultaneously.
</child>
<child>
<widget class="GtkCheckButton" id="auto_insertion_checkbutton">
- <property name="label" translatable="yes">Start downloading upon image device insertion</property>
+ <property name="label" translatable="yes">Start downloading upon device insertion</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -1826,7 +2373,7 @@ You can download photos from multiple image devices simultaneously.
</child>
<child>
<widget class="GtkCheckButton" id="auto_exit_checkbutton">
- <property name="label" translatable="yes">Exit program after completion of successful download</property>
+ <property name="label" translatable="yes">Exit program if download completes without any warnings or errors</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -1841,7 +2388,7 @@ You can download photos from multiple image devices simultaneously.
</child>
<child>
<widget class="GtkCheckButton" id="auto_delete_checkbutton">
- <property name="label" translatable="yes">Delete images from image device upon download completion</property>
+ <property name="label" translatable="yes">Delete photos and videos from device upon download completion</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -1902,7 +2449,7 @@ You can download photos from multiple image devices simultaneously.
</child>
</widget>
<packing>
- <property name="position">6</property>
+ <property name="position">8</property>
</packing>
</child>
<child>
@@ -1912,7 +2459,7 @@ You can download photos from multiple image devices simultaneously.
<property name="label" translatable="yes">Automation</property>
</widget>
<packing>
- <property name="position">6</property>
+ <property name="position">8</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
@@ -2005,7 +2552,7 @@ You can download photos from multiple image devices simultaneously.
<widget class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;Image Name Conflicts&lt;/b&gt;</property>
+ <property name="label" translatable="yes">&lt;b&gt;Photo and Video Name Conflicts&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
@@ -2131,7 +2678,7 @@ You can download photos from multiple image devices simultaneously.
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
- <property name="label" translatable="yes">Choose whether to skip downloading the image, or to add a unique indentifier.</property>
+ <property name="label" translatable="yes">Choose whether to skip downloading the file, or to add a unique indentifier.</property>
<property name="wrap">True</property>
</widget>
<packing>
@@ -2165,7 +2712,7 @@ You can download photos from multiple image devices simultaneously.
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
- <property name="label" translatable="yes">Specify what to do when an image of the same name has already been downloaded or backed up.</property>
+ <property name="label" translatable="yes">Specify what to do when a photo or video of the same name has already been downloaded or backed up.</property>
<property name="wrap">True</property>
</widget>
<packing>
@@ -2196,7 +2743,7 @@ You can download photos from multiple image devices simultaneously.
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
- <property name="label" translatable="yes">When backing up, choose whether to overwrite an image on the backup device that has the same name, or skip backing it up.</property>
+ <property name="label" translatable="yes">When backing up, choose whether to overwrite a file on the backup device that has the same name, or skip backing it up.</property>
<property name="wrap">True</property>
</widget>
<packing>
@@ -2298,7 +2845,7 @@ You can download photos from multiple image devices simultaneously.
</child>
</widget>
<packing>
- <property name="position">7</property>
+ <property name="position">9</property>
</packing>
</child>
<child>
@@ -2308,7 +2855,7 @@ You can download photos from multiple image devices simultaneously.
<property name="label" translatable="yes">Error Handling</property>
</widget>
<packing>
- <property name="position">7</property>
+ <property name="position">9</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
@@ -2382,7 +2929,7 @@ You can download photos from multiple image devices simultaneously.
<property name="type_hint">normal</property>
<property name="program_name">Rapid Photo Downloader</property>
<property name="copyright" translatable="yes">Copyright Damon Lynch 2007-10</property>
- <property name="comments" translatable="yes">Import your images efficiently and reliably</property>
+ <property name="comments" translatable="yes">Import your photos and videos efficiently and reliably</property>
<property name="website">http://www.damonlynch.net/rapid</property>
<property name="license" translatable="yes">Rapid Photo Downloader is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
@@ -2749,7 +3296,6 @@ Julien Valroff &lt;julien@kirya.net&gt;</property>
<child>
<widget class="GtkVSeparator" id="warning_vseparator">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
</widget>
<packing>
<property name="expand">False</property>
@@ -2806,7 +3352,6 @@ Julien Valroff &lt;julien@kirya.net&gt;</property>
<child>
<widget class="GtkVSeparator" id="vseparator2">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
</widget>
<packing>
<property name="expand">False</property>
diff --git a/rapid/glade3/video.svg b/rapid/glade3/video.svg
new file mode 100644
index 0000000..a03c672
--- /dev/null
+++ b/rapid/glade3/video.svg
@@ -0,0 +1,1638 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg4417"
+ sodipodi:version="0.32"
+ inkscape:version="0.45"
+ sodipodi:docbase="/home/dobey/Projects/gnome-icon-theme/scalable/mimetypes"
+ sodipodi:docname="video-x-generic.svg"
+ inkscape:export-filename="/home/andreas/project/gnome-icon-theme/scalable/categories/applications-multimedia.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4419">
+ <linearGradient
+ id="linearGradient2998">
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="0"
+ id="stop3001" />
+ <stop
+ id="stop3007"
+ offset="0.5"
+ style="stop-color:white;stop-opacity:1;" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop3003" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2996">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop2998" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop3000" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2992">
+ <stop
+ style="stop-color:black;stop-opacity:1;"
+ offset="0"
+ id="stop2994" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2996" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6593">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop6595" />
+ <stop
+ id="stop6601"
+ offset="0.5"
+ style="stop-color:#424a4d;stop-opacity:1;" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="1"
+ id="stop6597" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3010">
+ <stop
+ id="stop3012"
+ offset="0"
+ style="stop-color:white;stop-opacity:1;" />
+ <stop
+ id="stop3014"
+ offset="1"
+ style="stop-color:white;stop-opacity:0.4269006;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3002">
+ <stop
+ id="stop3004"
+ offset="0"
+ style="stop-color:#babdb6;stop-opacity:1" />
+ <stop
+ id="stop3006"
+ offset="1"
+ style="stop-color:#2e3436;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5737">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop5739" />
+ <stop
+ style="stop-color:white;stop-opacity:0.32163742;"
+ offset="1"
+ id="stop5741" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5683">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop5685" />
+ <stop
+ style="stop-color:#888a85;stop-opacity:1"
+ offset="1"
+ id="stop5687" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4790">
+ <stop
+ style="stop-color:#555753;stop-opacity:1"
+ offset="0"
+ id="stop4792" />
+ <stop
+ style="stop-color:#babdb6;stop-opacity:1"
+ offset="1"
+ id="stop4794" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3897">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop3899" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop3901" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3209">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop3211" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop3213" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3202">
+ <stop
+ id="stop3204"
+ offset="0"
+ style="stop-color:#babdb6;stop-opacity:1" />
+ <stop
+ id="stop3206"
+ offset="1"
+ style="stop-color:#555753;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3101">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop3103" />
+ <stop
+ id="stop3109"
+ offset="0.12824793"
+ style="stop-color:#555753;stop-opacity:1;" />
+ <stop
+ style="stop-color:#555753;stop-opacity:1;"
+ offset="0.56024098"
+ id="stop3111" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1"
+ offset="1"
+ id="stop3105" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3089">
+ <stop
+ style="stop-color:black;stop-opacity:1;"
+ offset="0"
+ id="stop3092" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1"
+ offset="1"
+ id="stop3094" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3032">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop3034" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop3036" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3169">
+ <stop
+ style="stop-color:black;stop-opacity:1;"
+ offset="0"
+ id="stop3171" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop3173" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3116">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop3118" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop3120" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3093">
+ <stop
+ style="stop-color:#555753;stop-opacity:1;"
+ offset="0"
+ id="stop3095" />
+ <stop
+ id="stop2022"
+ offset="0.72289157"
+ style="stop-color:#888a85;stop-opacity:1;" />
+ <stop
+ style="stop-color:#71736e;stop-opacity:1;"
+ offset="1"
+ id="stop3097" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3041">
+ <stop
+ style="stop-color:#555753;stop-opacity:1;"
+ offset="0"
+ id="stop3043" />
+ <stop
+ id="stop3049"
+ offset="0.32630172"
+ style="stop-color:#898b86;stop-opacity:1;" />
+ <stop
+ style="stop-color:#525450;stop-opacity:1;"
+ offset="0.62983823"
+ id="stop3051" />
+ <stop
+ id="stop3053"
+ offset="0.85847878"
+ style="stop-color:#535551;stop-opacity:1;" />
+ <stop
+ style="stop-color:#3e3f3d;stop-opacity:1;"
+ offset="1"
+ id="stop3045" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2982">
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="0"
+ id="stop2984" />
+ <stop
+ id="stop2990"
+ offset="0.0547975"
+ style="stop-color:black;stop-opacity:1;" />
+ <stop
+ style="stop-color:black;stop-opacity:1;"
+ offset="0.90963858"
+ id="stop2992" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2986" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2927">
+ <stop
+ style="stop-color:#ddd;stop-opacity:1;"
+ offset="0"
+ id="stop2929" />
+ <stop
+ style="stop-color:#ddd;stop-opacity:0;"
+ offset="1"
+ id="stop2931" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2919">
+ <stop
+ style="stop-color:#646464;stop-opacity:1;"
+ offset="0"
+ id="stop2921" />
+ <stop
+ style="stop-color:#646464;stop-opacity:0;"
+ offset="1"
+ id="stop2923" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2911">
+ <stop
+ style="stop-color:#464f52;stop-opacity:1;"
+ offset="0"
+ id="stop2913" />
+ <stop
+ style="stop-color:#464f52;stop-opacity:0;"
+ offset="1"
+ id="stop2915" />
+ </linearGradient>
+ <linearGradient
+ id="aigrd6"
+ gradientUnits="userSpaceOnUse"
+ x1="-1.2432"
+ y1="-3.2012"
+ x2="26.5278"
+ y2="28.3922">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop6468" />
+ <stop
+ offset="1"
+ style="stop-color:#959595"
+ id="stop6470" />
+ </linearGradient>
+ <radialGradient
+ id="aigrd3"
+ cx="23.1567"
+ cy="29.0337"
+ r="26.966"
+ fx="23.1567"
+ fy="29.0337"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#767676"
+ id="stop6443" />
+ <stop
+ offset="1"
+ style="stop-color:#484848"
+ id="stop6445" />
+ </radialGradient>
+ <radialGradient
+ id="aigrd2"
+ cx="13.5024"
+ cy="12.3457"
+ r="10.8808"
+ fx="13.5024"
+ fy="12.3457"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#454545"
+ id="stop6436" />
+ <stop
+ offset="1"
+ style="stop-color:#333333"
+ id="stop6438" />
+ </radialGradient>
+ <linearGradient
+ y2="248.6311"
+ x2="153.0005"
+ y1="15.4238"
+ x1="99.7773"
+ gradientUnits="userSpaceOnUse"
+ id="aigrd1">
+ <stop
+ id="stop53300"
+ style="stop-color:#707070;stop-opacity:1;"
+ offset="0" />
+ <stop
+ id="stop53302"
+ style="stop-color:#cdcdcd;stop-opacity:1;"
+ offset="1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#aigrd3"
+ id="radialGradient4553"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.101263,0,0,1.101263,-57.56432,3.17435)"
+ cx="23.1567"
+ cy="29.0337"
+ fx="23.1567"
+ fy="29.0337"
+ r="26.966" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#aigrd2"
+ id="radialGradient4556"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.101263,0,0,1.101263,-57.56432,3.17435)"
+ cx="13.5024"
+ cy="12.3457"
+ fx="13.5024"
+ fy="12.3457"
+ r="10.8808" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#aigrd6"
+ id="linearGradient6380"
+ x1="0.88337302"
+ y1="-37.643921"
+ x2="30.994286"
+ y2="-0.67233789"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-57.87868,40.00204)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#aigrd1"
+ id="linearGradient2001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(3.505583,42.36313)"
+ x1="4.6333733"
+ y1="-33.518921"
+ x2="34.056786"
+ y2="2.1401622" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2911"
+ id="linearGradient2917"
+ x1="29.75"
+ y1="38.420128"
+ x2="29.75"
+ y2="43.9375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2919"
+ id="linearGradient2925"
+ x1="34.25"
+ y1="38.804165"
+ x2="34.25"
+ y2="44.380974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2927"
+ id="linearGradient2933"
+ x1="35.625"
+ y1="38.874573"
+ x2="35.625"
+ y2="43.444958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2982"
+ id="linearGradient2988"
+ x1="61.933079"
+ y1="31.25"
+ x2="106.09679"
+ y2="31.25"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2982"
+ id="linearGradient3000"
+ x1="62.3125"
+ y1="40"
+ x2="106.06924"
+ y2="40"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3041"
+ id="linearGradient3047"
+ x1="63.5"
+ y1="29.4375"
+ x2="104.79548"
+ y2="29.4375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3093"
+ id="linearGradient3099"
+ x1="91.339241"
+ y1="19.333946"
+ x2="105.30366"
+ y2="19.333946"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3116"
+ id="linearGradient3122"
+ x1="75.875"
+ y1="28.625"
+ x2="77.375"
+ y2="39.40625"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3202"
+ id="linearGradient3072"
+ x1="73.337959"
+ y1="25.384384"
+ x2="76.955917"
+ y2="12.306049"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,-1)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3032"
+ id="linearGradient3087"
+ gradientUnits="userSpaceOnUse"
+ x1="77.000015"
+ y1="13.524997"
+ x2="80.39064"
+ y2="-5.0196266" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3089"
+ id="linearGradient3096"
+ x1="87.5"
+ y1="13.75"
+ x2="90.249092"
+ y2="14.446928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3101"
+ id="linearGradient3107"
+ x1="76.937096"
+ y1="15.875"
+ x2="93.063683"
+ y2="15.875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-60,0)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2982"
+ id="linearGradient3060"
+ x1="18.738331"
+ y1="16.695379"
+ x2="44.680313"
+ y2="23.499962"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2982"
+ id="linearGradient3062"
+ x1="18.738331"
+ y1="15.695379"
+ x2="44.680313"
+ y2="22.499962"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3169"
+ id="radialGradient3143"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.970504,0,0,0.407216,-56.50939,11.32708)"
+ cx="85.500011"
+ cy="16.999587"
+ fx="85.500011"
+ fy="16.999587"
+ r="22.249191" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3209"
+ id="linearGradient3215"
+ x1="82.489655"
+ y1="14.582699"
+ x2="80.921646"
+ y2="5.6930151"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3897"
+ id="linearGradient3903"
+ x1="76.781258"
+ y1="15.414051"
+ x2="81.75782"
+ y2="2.0453157"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4790"
+ id="linearGradient4796"
+ x1="-26.853115"
+ y1="10.70663"
+ x2="-29.875"
+ y2="24.003584"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5683"
+ id="radialGradient5689"
+ cx="13.67201"
+ cy="7.8948545"
+ fx="13.67201"
+ fy="7.8948545"
+ r="17.503583"
+ gradientTransform="matrix(2.396145,0,0,1.213358,-19.88512,-7.579285)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5737"
+ id="linearGradient5743"
+ x1="81.92186"
+ y1="14.49374"
+ x2="81.690651"
+ y2="3.1698563"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3002"
+ id="radialGradient3907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.03989,0,0,1.032958,-64.86939,-4.409191)"
+ cx="13.171977"
+ cy="21.697666"
+ fx="13.171977"
+ fy="21.697666"
+ r="17.503583" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3010"
+ id="linearGradient3909"
+ gradientUnits="userSpaceOnUse"
+ x1="80.992172"
+ y1="15.317177"
+ x2="84.55397"
+ y2="5.7076802" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6593"
+ id="linearGradient6599"
+ x1="26.641592"
+ y1="8.5011101"
+ x2="24.358414"
+ y2="10.498891"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2992"
+ id="radialGradient2998"
+ cx="25.546875"
+ cy="10"
+ fx="25.546875"
+ fy="10"
+ r="8.453125"
+ gradientTransform="matrix(1,0,0,0.469501,0,5.304991)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2996"
+ id="linearGradient3003"
+ x1="25.5"
+ y1="15.453269"
+ x2="24.942444"
+ y2="1.2227453"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2998"
+ id="linearGradient3005"
+ x1="22.4375"
+ y1="32.25"
+ x2="28.25"
+ y2="31.75"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(14.75,-0.75)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2998"
+ id="linearGradient3017"
+ x1="44.331707"
+ y1="22.49238"
+ x2="46.908863"
+ y2="22.693205"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#d8d8d8"
+ borderopacity="1"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="61.621338"
+ inkscape:cy="29.26585"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ showguides="false"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="997"
+ inkscape:window-height="743"
+ inkscape:window-x="628"
+ inkscape:window-y="223"
+ inkscape:showpageshadow="false"
+ showborder="true"
+ inkscape:object-nodes="false"
+ inkscape:object-paths="false"
+ inkscape:grid-points="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="13.5"
+ id="guide2099" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="21.4375"
+ id="guide2101" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="29.4375"
+ id="guide2103" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="37.375"
+ id="guide2105" />
+ <sodipodi:guide
+ orientation="horizontal"
+ position="47"
+ id="guide2107" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="9.9878833"
+ id="guide2133" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="20.32932"
+ id="guide2135" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="30.670757"
+ id="guide2137" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="40.967999"
+ id="guide2139" />
+ <sodipodi:guide
+ orientation="horizontal"
+ position="47.729708"
+ id="guide3008" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4422">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title>Applications Multimedia</dc:title>
+ <dc:date>August 2006</dc:date>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lapo Calamandrei</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source>http://www.gnome.org</dc:source>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>multimedia</rdf:li>
+ <rdf:li>sound</rdf:li>
+ <rdf:li>video</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+ <dc:contributor>
+ <cc:Agent>
+ <dc:title>Jakub Steiner, Andreas Nilsson</dc:title>
+ </cc:Agent>
+ </dc:contributor>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/SourceCode" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ style="display:inline">
+ <path
+ style="opacity:0.8;color:black;fill:url(#radialGradient3143);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 37.265718,10.402843 C 26.947159,7.9031525 13.725313,9.3896756 7.7678855,13.719266 C 1.8104576,18.048857 5.3532386,23.59664 15.671801,26.09633 C 25.990362,28.596021 39.212206,27.109496 45.16963,22.779906 C 51.127064,18.450317 47.584288,12.902533 37.265718,10.402843 z "
+ id="path3141"
+ sodipodi:nodetypes="csssc" />
+ <path
+ style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 26.513532,3.069333 C 27.494068,3.144641 27.625715,3.264127 27.593697,3.379126 L 25.353295,7.201505 C 25.241199,7.392753 24.858508,7.513819 24.307494,7.605839 C 23.749135,7.647763 23.314524,7.628665 23.015452,7.491943 L 16.895253,4.686457 C 16.661667,4.546046 16.821602,4.319954 19.490909,3.569783 C 20.984007,3.258201 22.590966,3.065644 24.257151,3 C 25.237216,3.014582 26.030543,3.032239 26.513532,3.069333 z M 31.282791,3.653536 C 31.750192,3.705773 32.579862,3.871181 33.795668,4.168406 C 35.16683,4.606105 36.347892,5.140176 37.300167,5.751299 C 38.791373,7.011106 38.682595,7.243464 38.326732,7.311671 L 29.958044,8.342364 C 29.523357,8.395612 29.110469,8.293131 28.640151,8.106981 C 28.310696,7.916013 28.145983,7.735758 28.251072,7.556464 L 30.491474,3.734084 C 30.569914,3.648554 30.74289,3.593199 31.282791,3.653536 z M 14.624127,5.635824 C 14.672763,5.642736 14.726595,5.654075 14.766964,5.66811 L 20.895251,8.459797 C 21.198262,8.598318 21.220027,8.804396 21.123149,9.064521 C 20.922542,9.31194 20.678301,9.485432 20.26438,9.536135 L 11.903779,10.553029 C 11.524573,10.575096 11.159907,10.395586 11.066412,9.001043 C 11.140424,8.627402 11.274477,8.259682 11.494076,7.885023 C 11.713674,7.510364 11.999766,7.161268 12.344331,6.820768 C 13.737443,5.79183 14.306188,5.590641 14.624127,5.635824 z M 39.255235,8.652972 C 39.579997,8.686515 39.852498,8.990752 39.933588,10.20027 C 39.859576,10.573911 39.725523,10.941631 39.505924,11.31629 C 39.286326,11.69095 39.000234,12.040045 38.655669,12.380545 C 37.04945,13.566883 36.537303,13.638989 36.233036,13.533203 L 30.104749,10.741516 C 29.81764,10.610264 29.776139,10.418878 29.852587,10.17819 C 29.862751,10.164742 29.866324,10.149776 29.876851,10.136792 C 29.881935,10.123141 29.895209,10.109364 29.901116,10.095394 C 30.098159,9.869478 30.343421,9.71322 30.73562,9.665178 L 39.096221,8.648284 C 39.146533,8.645356 39.205556,8.647841 39.255235,8.652972 z M 21.647827,10.874004 C 21.852925,10.908941 22.06662,10.973673 22.28547,11.055641 C 22.662017,11.261281 22.861024,11.453602 22.748928,11.644849 L 20.508526,15.467229 C 20.362179,15.626804 19.824526,15.673458 17.204332,15.032907 C 15.833169,14.595211 14.652109,14.061136 13.699833,13.450014 C 12.20863,12.190207 12.317406,11.957847 12.673268,11.889642 L 21.041956,10.858949 C 21.246227,10.833927 21.442729,10.839068 21.647827,10.874004 z M 27.473567,11.587619 C 27.677762,11.605656 27.840994,11.643744 27.984548,11.709371 L 34.104747,14.514857 C 34.338335,14.655265 34.1784,14.881358 31.509091,15.63153 C 30.015994,15.943111 28.409033,16.135671 26.742849,16.201313 C 23.773106,16.157133 23.358511,15.993835 23.406303,15.822188 L 25.646705,11.999808 C 25.763112,11.801204 26.173739,11.68218 26.760965,11.589069 C 27.024213,11.571596 27.269373,11.569582 27.473567,11.587619 z "
+ id="path2995"
+ sodipodi:nodetypes="cccccccccccccccccccccccccsccccscccsssccccccccccccccccccccc" />
+ <g
+ id="g3901"
+ transform="matrix(0.976118,0,0,0.976118,49.41488,6.4418)">
+ <path
+ sodipodi:nodetypes="cccccccccccccccccccccccccsccccscccsssccccccccccccccccccccc"
+ id="path2984"
+ d="M -23.486468,3.069333 C -22.505932,3.144641 -22.374285,3.264127 -22.406303,3.379126 L -24.646705,7.201505 C -24.758801,7.392753 -25.141492,7.513819 -25.692506,7.605839 C -26.250865,7.647763 -26.685476,7.628665 -26.984548,7.491943 L -33.104747,4.686457 C -33.338333,4.546046 -33.178398,4.319954 -30.509091,3.569783 C -29.015993,3.258201 -27.409034,3.065644 -25.742849,3 C -24.762784,3.014582 -23.969457,3.032239 -23.486468,3.069333 z M -18.717209,3.653536 C -18.249808,3.705773 -17.420138,3.871181 -16.204332,4.168406 C -14.83317,4.606105 -13.652108,5.140176 -12.699833,5.751299 C -11.208627,7.011106 -11.317405,7.243464 -11.673268,7.311671 L -20.041956,8.342364 C -20.476643,8.395612 -20.889531,8.293131 -21.359849,8.106981 C -21.689304,7.916013 -21.854017,7.735758 -21.748928,7.556464 L -19.508526,3.734084 C -19.430086,3.648554 -19.25711,3.593199 -18.717209,3.653536 z M -35.375873,5.635824 C -35.327237,5.642736 -35.273405,5.654075 -35.233036,5.66811 L -29.104749,8.459797 C -28.801738,8.598318 -28.779973,8.804396 -28.876851,9.064521 C -29.077458,9.31194 -29.321699,9.485432 -29.73562,9.536135 L -38.096221,10.553029 C -38.475427,10.575096 -38.840093,10.395586 -38.933588,9.001043 C -38.859576,8.627402 -38.725523,8.259682 -38.505924,7.885023 C -38.286326,7.510364 -38.000234,7.161268 -37.655669,6.820768 C -36.262557,5.79183 -35.693812,5.590641 -35.375873,5.635824 z M -10.744765,8.652972 C -10.420003,8.686515 -10.147502,8.990752 -10.066412,10.20027 C -10.140424,10.573911 -10.274477,10.941631 -10.494076,11.31629 C -10.713674,11.69095 -10.999766,12.040045 -11.344331,12.380545 C -12.95055,13.566883 -13.462697,13.638989 -13.766964,13.533203 L -19.895251,10.741516 C -20.18236,10.610264 -20.223861,10.418878 -20.147413,10.17819 C -20.137249,10.164742 -20.133676,10.149776 -20.123149,10.136792 C -20.118065,10.123141 -20.104791,10.109364 -20.098884,10.095394 C -19.901841,9.869478 -19.656579,9.71322 -19.26438,9.665178 L -10.903779,8.648284 C -10.853467,8.645356 -10.794444,8.647841 -10.744765,8.652972 z M -28.352173,10.874004 C -28.147075,10.908941 -27.93338,10.973673 -27.71453,11.055641 C -27.337983,11.261281 -27.138976,11.453602 -27.251072,11.644849 L -29.491474,15.467229 C -29.637821,15.626804 -30.175474,15.673458 -32.795668,15.032907 C -34.166831,14.595211 -35.347891,14.061136 -36.300167,13.450014 C -37.79137,12.190207 -37.682594,11.957847 -37.326732,11.889642 L -28.958044,10.858949 C -28.753773,10.833927 -28.557271,10.839068 -28.352173,10.874004 z M -22.526433,11.587619 C -22.322238,11.605656 -22.159006,11.643744 -22.015452,11.709371 L -15.895253,14.514857 C -15.661665,14.655265 -15.8216,14.881358 -18.490909,15.63153 C -19.984006,15.943111 -21.590967,16.135671 -23.257151,16.201313 C -26.226894,16.157133 -26.641489,15.993835 -26.593697,15.822188 L -24.353295,11.999808 C -24.236888,11.801204 -23.826261,11.68218 -23.239035,11.589069 C -22.975787,11.571596 -22.730627,11.569582 -22.526433,11.587619 z "
+ style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:2.04893351;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <path
+ sodipodi:nodetypes="csssccccccccccccccccccccccccccsccccscccsssccccccccccccccccccccc"
+ id="path2998"
+ d="M -19.970667,2.274237 C -29.301506,1.13127 -38.90351,3.665589 -41.403702,7.931199 C -43.903895,12.196811 -38.360174,16.58638 -29.029333,17.729347 C -19.698494,18.872313 -10.096492,16.337996 -7.596298,12.072384 C -5.096106,7.806773 -10.639827,3.417203 -19.970667,2.274237 z M -23.486468,3 C -22.505932,3.075308 -22.374285,3.194794 -22.406303,3.309793 L -24.646705,7.132172 C -24.758801,7.32342 -25.141492,7.444486 -25.692506,7.536506 C -26.250865,7.57843 -26.685476,7.559332 -26.984548,7.42261 L -33.104747,4.617124 C -33.338333,4.476713 -33.178398,4.250621 -30.509091,3.50045 C -29.015993,3.188868 -27.409034,2.996311 -25.742849,2.930667 C -24.762784,2.945249 -23.969457,2.962906 -23.486468,3 z M -18.717209,3.584203 C -18.249808,3.63644 -17.420138,3.801848 -16.204332,4.099073 C -14.83317,4.536772 -13.652108,5.070843 -12.699833,5.681966 C -11.208627,6.941773 -11.317405,7.174131 -11.673268,7.242338 L -20.041956,8.273031 C -20.476643,8.326279 -20.889531,8.223798 -21.359849,8.037648 C -21.689304,7.84668 -21.854017,7.666425 -21.748928,7.487131 L -19.508526,3.664751 C -19.430086,3.579221 -19.25711,3.523866 -18.717209,3.584203 z M -35.375873,5.566491 C -35.327237,5.573403 -35.273405,5.584742 -35.233036,5.598777 L -29.104749,8.390464 C -28.801738,8.528985 -28.779973,8.735063 -28.876851,8.995188 C -29.077458,9.242607 -29.321699,9.416099 -29.73562,9.466802 L -38.096221,10.483696 C -38.475427,10.505763 -38.840093,10.326253 -38.933588,8.93171 C -38.859576,8.558069 -38.725523,8.190349 -38.505924,7.81569 C -38.286326,7.441031 -38.000234,7.091935 -37.655669,6.751435 C -36.262557,5.722497 -35.693812,5.521308 -35.375873,5.566491 z M -10.744765,8.583639 C -10.420003,8.617182 -10.147502,8.921419 -10.066412,10.130937 C -10.140424,10.504578 -10.274477,10.872298 -10.494076,11.246957 C -10.713674,11.621617 -10.999766,11.970712 -11.344331,12.311212 C -12.95055,13.49755 -13.462697,13.569656 -13.766964,13.46387 L -19.895251,10.672183 C -20.18236,10.540931 -20.223861,10.349545 -20.147413,10.108857 C -20.137249,10.095409 -20.133676,10.080443 -20.123149,10.067459 C -20.118065,10.053808 -20.104791,10.040031 -20.098884,10.026061 C -19.901841,9.800145 -19.656579,9.643887 -19.26438,9.595845 L -10.903779,8.578951 C -10.853467,8.576023 -10.794444,8.578508 -10.744765,8.583639 z M -28.352173,10.804671 C -28.147075,10.839608 -27.93338,10.90434 -27.71453,10.986308 C -27.337983,11.191948 -27.138976,11.384269 -27.251072,11.575516 L -29.491474,15.397896 C -29.637821,15.557471 -30.175474,15.604125 -32.795668,14.963574 C -34.166831,14.525878 -35.347891,13.991803 -36.300167,13.380681 C -37.79137,12.120874 -37.682594,11.888514 -37.326732,11.820309 L -28.958044,10.789616 C -28.753773,10.764594 -28.557271,10.769735 -28.352173,10.804671 z M -22.526433,11.518286 C -22.322238,11.536323 -22.159006,11.574411 -22.015452,11.640038 L -15.895253,14.445524 C -15.661665,14.585932 -15.8216,14.812025 -18.490909,15.562197 C -19.984006,15.873778 -21.590967,16.066338 -23.257151,16.13198 C -26.226894,16.0878 -26.641489,15.924502 -26.593697,15.752855 L -24.353295,11.930475 C -24.236888,11.731871 -23.826261,11.612847 -23.239035,11.519736 C -22.975787,11.502263 -22.730627,11.500249 -22.526433,11.518286 z "
+ style="opacity:1;color:black;fill:url(#radialGradient3907);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <path
+ transform="matrix(1.210083,0,0,1.462367,-127.7352,-5.811834)"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ sodipodi:ry="5.8125"
+ sodipodi:rx="14.875"
+ sodipodi:cy="10.8125"
+ sodipodi:cx="85.3125"
+ id="path2986"
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#555753;stroke-width:0.77012676;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.142856,0,0,1.290324,-121.9999,-3.951621)"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ sodipodi:ry="5.8125"
+ sodipodi:rx="14.875"
+ sodipodi:cy="10.8125"
+ sodipodi:cx="85.3125"
+ id="path2988"
+ style="opacity:0.5;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3909);stroke-width:0.84363157;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ style="color:black;fill:url(#linearGradient3107);fill-opacity:1;fill-rule:nonzero;stroke:black;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 25.5,5.5 C 20.536155,5.5 16.5,7.52016 16.5,10 L 16.5,16 C 16.5,18.47984 20.536157,20.5 25.5,20.5 C 30.463845,20.5 34.5,18.47984 34.5,16 L 34.5,10 C 34.5,7.52016 30.463848,5.5 25.5,5.5 z "
+ id="path2003"
+ sodipodi:nodetypes="csssssc" />
+ <path
+ style="opacity:1;fill:url(#linearGradient3099);fill-opacity:1;stroke:url(#linearGradient3096);stroke-width:1.01100004;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 22.812512,20.28125 L 41.60134,24.28125 C 46.85523,25.399764 46.49128,29.361359 46.49128,29.361359 L 46.49128,21.486359 C 46.49128,21.486359 46.132703,17.663667 41.60134,16.8125 L 24,13.506282"
+ id="rect2953"
+ sodipodi:nodetypes="czcczc" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:black;fill:url(#linearGradient6599);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path5706"
+ sodipodi:cx="25.5"
+ sodipodi:cy="9.5"
+ sodipodi:rx="8.5"
+ sodipodi:ry="3.5"
+ d="M 34 9.5 A 8.5 3.5 0 1 1 17,9.5 A 8.5 3.5 0 1 1 34 9.5 z"
+ transform="matrix(1,0,0,1.142857,0,-0.857143)" />
+ <path
+ transform="translate(0,-1)"
+ style="opacity:0.4;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3060);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0.7;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 17.5625,16 C 17.5625,16.536843 17.92744,17.18169 18.875,17.8125 C 19.82256,18.44331 21.26461,18.980918 22.96875,19.25 C 22.989579,19.249384 23.010421,19.249384 23.03125,19.25 L 41.8125,23.25 C 43.42996,23.594345 44.694406,24.079834 45.5625,24.8125"
+ id="path3043"
+ sodipodi:nodetypes="cssccc" />
+ <path
+ style="opacity:0.39849626;color:black;fill:url(#radialGradient2998);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:1.96992087;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 26.34375,6.03125 L 25.28125,7.84375 C 25.169154,8.034998 24.801014,8.15798 24.25,8.25 C 23.691642,8.291924 23.267822,8.292972 22.96875,8.15625 L 20.21875,6.90625 C 19.435475,7.2019093 18.739204,7.5309064 18.21875,7.9375 L 20.84375,9.125 C 21.146761,9.2635209 21.159378,9.458625 21.0625,9.71875 C 20.861894,9.9661689 20.632671,10.136797 20.21875,10.1875 L 17.09375,10.5625 C 17.234387,11.031002 17.545294,11.473239 18,11.875 L 20.96875,11.5 C 21.173021,11.474978 21.388652,11.496314 21.59375,11.53125 C 21.798848,11.566187 21.9999,11.636782 22.21875,11.71875 C 22.595298,11.92439 22.799596,12.121253 22.6875,12.3125 L 21.90625,13.625 C 22.800402,13.822195 23.781767,13.929355 24.8125,13.96875 L 25.59375,12.65625 C 25.710156,12.457646 26.100274,12.343111 26.6875,12.25 C 26.950747,12.232527 27.202056,12.231963 27.40625,12.25 C 27.610446,12.268037 27.793946,12.309373 27.9375,12.375 L 30.09375,13.34375 C 30.931804,13.088908 31.680434,12.779683 32.28125,12.40625 L 30.03125,11.40625 C 29.74414,11.274998 29.704802,11.084438 29.78125,10.84375 C 29.791414,10.830302 29.801973,10.794234 29.8125,10.78125 C 29.817585,10.767599 29.837843,10.76397 29.84375,10.75 C 30.040793,10.524084 30.295301,10.360542 30.6875,10.3125 L 34,9.90625 C 33.97667,9.4345321 33.779459,8.9803565 33.4375,8.5625 L 29.90625,9 C 29.471563,9.0532484 29.064068,8.93615 28.59375,8.75 C 28.264296,8.5590324 28.082411,8.398044 28.1875,8.21875 L 29.25,6.4375 C 28.355889,6.2289546 27.381597,6.0799454 26.34375,6.03125 z "
+ id="path3099" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1.0574123"
+ inkscape:original="M 25.5 5.5 C 20.536155 5.5 16.5 7.52016 16.5 10 L 16.5 16 C 16.5 18.009253 19.166589 19.705564 22.8125 20.28125 L 41.59375 24.28125 C 46.847639 25.399764 46.5 29.375 46.5 29.375 L 46.5 21.5 C 46.5 21.5 46.14223 17.566861 41.59375 16.8125 L 34.5 15.625 L 34.5 10 C 34.5 7.52016 30.463848 5.5 25.5 5.5 z "
+ style="opacity:0.1;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:white;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3030"
+ d="M 25.5,6.5625 C 23.161524,6.5625 21.062537,7.0622517 19.625,7.78125 C 18.187463,8.5002483 17.5625,9.3337568 17.5625,10 L 17.5625,16 C 17.5625,16.536843 17.92744,17.18169 18.875,17.8125 C 19.82256,18.44331 21.26461,18.980918 22.96875,19.25 C 22.989579,19.249384 23.010421,19.249384 23.03125,19.25 L 41.8125,23.25 C 43.42996,23.594345 44.569406,24.236084 45.4375,24.96875 L 45.4375,21.59375 C 45.4375,21.59375 45.374858,20.869771 44.84375,20 C 44.312642,19.130229 43.392274,18.17313 41.40625,17.84375 L 34.3125,16.65625 C 33.810416,16.5683 33.442516,16.134704 33.4375,15.625 L 33.4375,10 C 33.4375,9.3337563 32.812537,8.5002482 31.375,7.78125 C 29.937463,7.0622518 27.838477,6.5625 25.5,6.5625 z " />
+ <g
+ id="g3035">
+ <path
+ sodipodi:nodetypes="cczcczccc"
+ id="rect2949"
+ d="M 1.50002,29.499973 C 1.50002,29.499973 3.532822,27.499973 8.506322,27.499973 L 31.512623,27.499973 C 44.18377,27.499973 46.50001,25.003118 46.50001,21.499973 C 46.50001,24.174151 46.50001,29.499986 46.50001,29.499986 C 46.50001,35.359796 44.22293,37.947515 31.512623,38.499986 L 8.506322,39.499986 C 3.847089,39.941928 1.50002,43.499986 1.50002,43.499986 L 1.50002,29.499973 z "
+ style="opacity:1;fill:url(#linearGradient3047);fill-opacity:1;stroke:#2e3436;stroke-width:0.99999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cczc"
+ id="path2978"
+ d="M 2.12502,31.874973 C 2.12502,31.874973 3.532822,29.499973 8.506322,29.499973 L 31.512623,29.499973 C 44.18377,29.499973 46.50001,27.003118 46.50001,23.499973"
+ style="opacity:0.6;fill:none;fill-opacity:1;stroke:url(#linearGradient2988);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0.5;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="czcc"
+ id="path2980"
+ d="M 46.56251,25.968736 C 46.56251,31.828546 45.93712,36.499986 31.512623,36.499986 L 8.506322,37.499986 C 3.847089,37.941928 1.50002,41.499986 1.50002,41.499986"
+ style="opacity:0.6;fill:none;fill-opacity:1;stroke:url(#linearGradient3000);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0.9;stroke-opacity:1" />
+ <path
+ id="path3004"
+ d="M 45.0625,28.71875 C 44.37308,29.200323 43.74836,29.777176 42,30.21875 L 42,33.4375 C 45.16645,32.177079 45.06242,30.62266 45.0625,30.59375 L 45.0625,28.71875 z M 41,30.40625 C 40.20657,30.55277 39.21189,30.683386 38,30.78125 L 38,34.46875 C 39.187273,34.27185 40.17881,34.037517 41,33.78125 L 41,30.40625 z M 37,30.84375 C 35.848926,30.914726 34.565444,30.981472 33,31 L 33,34.96875 C 34.511976,34.894324 35.84389,34.78099 37,34.625 L 37,30.84375 z M 32,31 L 28,31.03125 L 28,35.1875 L 32,35.03125 L 32,31 z M 27,31.03125 L 23,31.0625 L 23,35.375 L 27,35.21875 L 27,31.03125 z M 22,31.09375 L 18,31.125 L 18,35.59375 L 22,35.4375 L 22,31.09375 z M 17,31.125 L 13,31.15625 L 13,35.78125 L 17,35.625 L 17,31.125 z M 12,31.15625 L 8.34375,31.1875 C 8.225387,31.184386 8.117212,31.186035 8,31.1875 L 8,36.03125 C 8.163488,36.008835 8.337513,35.974104 8.46875,35.96875 L 12,35.8125 L 12,31.15625 z M 7,31.25 C 5.871329,31.386685 4.830104,31.709176 4,32.125 L 4,37.375 C 4.94283,36.868709 6.054189,36.456108 7,36.21875 L 7,31.25 z M 3,32.75 C 2.399986,33.209949 2.017888,33.69653 2,34.125 L 2,39.25 C 2.138532,38.805351 2.509781,38.379775 3,38 L 3,32.75 z "
+ style="opacity:0.3;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:0.99999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ transform="translate(-60,0)"
+ d="M 105.5,25.8125 C 105.11869,26.131585 104.70169,26.466625 104.1875,26.71875 C 101.75082,27.913525 97.901254,28.5 91.5,28.5 L 68.5,28.5 C 66.147294,28.5 64.53517,28.961373 63.53125,29.40625 C 62.760319,29.74788 62.632868,29.916333 62.5,30.03125 L 62.5,41.03125 C 63.789632,39.926876 65.68087,38.758511 68.40625,38.5 C 68.427078,38.499349 68.447922,38.499349 68.46875,38.5 L 91.46875,37.5 C 97.758835,37.226593 101.36312,36.399909 103.21875,35.15625 C 105.07438,33.912591 105.5,32.299656 105.5,29.5 C 105.5,29.5 105.5,27.294332 105.5,25.8125 z "
+ id="path3065"
+ style="opacity:0.2;fill:none;fill-opacity:1;stroke:url(#linearGradient3122);stroke-width:0.99999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ inkscape:original="M 106.5 21.5 C 106.5 25.003145 104.17114 27.5 91.5 27.5 L 68.5 27.5 C 63.5265 27.5 61.500001 29.5 61.5 29.5 L 61.5 43.5 C 61.5 43.5 63.840764 39.941943 68.5 39.5 L 91.5 38.5 C 104.21031 37.947529 106.5 35.359809 106.5 29.5 C 106.5 29.5 106.5 24.174178 106.5 21.5 z "
+ inkscape:radius="-1"
+ sodipodi:type="inkscape:offset" />
+ <rect
+ ry="0.47940475"
+ rx="0.42824069"
+ y="29.53125"
+ x="2.0182304"
+ height="12.03125"
+ width="0.96354169"
+ id="rect3114"
+ style="opacity:1;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1.01100004;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.9;stroke-opacity:1" />
+ </g>
+ <path
+ style="opacity:0.4;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3062);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 44.84375,21 C 44.312642,20.130229 43.392274,19.17313 41.40625,18.84375 L 34.3125,17.65625 C 28.132886,16.621777 17.500893,15.478056 17.500893,11.390758"
+ id="path3046"
+ sodipodi:nodetypes="cczc" />
+ <path
+ style="opacity:0.15;color:black;fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 18,13.875 L 18,15.90625 L 20,16.65625 L 20,14.78125 C 19.159795,14.440675 18.469808,14.133901 18,13.875 z M 21,15.15625 L 21,17.03125 L 23,17.78125 L 23,15.75 C 22.445474,15.648993 21.73377,15.420922 21,15.15625 z M 24,15.96875 L 24,18 L 26,18.40625 L 26,16.375 L 24,15.96875 z M 27,16.5625 L 27,18.59375 L 29,19 L 29,16.96875 L 27,16.5625 z M 30,17.1875 L 30,19.21875 L 32,19.625 L 32,17.59375 L 30,17.1875 z M 33,17.78125 L 33,19.8125 L 35,20.21875 L 35,18.21875 L 33,17.78125 z M 36,18.40625 L 36,20.4375 L 38,20.84375 L 38,18.8125 L 36,18.40625 z M 39,19.03125 L 39,21.03125 L 41,21.4375 L 41,19.4375 L 39,19.03125 z M 42,19.625 L 42,21.6875 C 42.817262,21.904793 43.456287,22.197387 44,22.59375 L 44,20.3125 C 43.403373,20.028973 42.747736,19.784187 42,19.625 z M 45,20.875 L 45,23.46875 L 45.4375,23.90625 L 46,22.15625 L 46,21.6875 C 45.689896,21.392151 45.36,21.118532 45,20.875 z "
+ id="path3071" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3087);stroke-width:0.82348335;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path2966"
+ sodipodi:cx="85.3125"
+ sodipodi:cy="10.8125"
+ sodipodi:rx="14.875"
+ sodipodi:ry="5.8125"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ transform="matrix(1.142857,0,0,1.290323,-72.00001,-3.95161)" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:0.75173426;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3165"
+ sodipodi:cx="85.3125"
+ sodipodi:cy="10.8125"
+ sodipodi:rx="14.875"
+ sodipodi:ry="5.8125"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ transform="matrix(1.210083,0,0,1.462367,-77.73519,-5.811834)" />
+ <path
+ style="opacity:1;color:black;fill:url(#radialGradient5689);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 30.029333,2.274237 C 20.698494,1.13127 11.09649,3.665589 8.596298,7.931199 C 6.0961053,12.196811 11.639826,16.58638 20.970667,17.729347 C 30.301506,18.872313 39.903508,16.337996 42.403702,12.072384 C 44.903894,7.806773 39.360173,3.417203 30.029333,2.274237 z M 26.513532,3 C 27.494068,3.075308 27.625715,3.194794 27.593697,3.309793 L 25.353295,7.132172 C 25.241199,7.32342 24.858508,7.444486 24.307494,7.536506 C 23.749135,7.57843 23.314524,7.559332 23.015452,7.42261 L 16.895253,4.617124 C 16.661667,4.476713 16.821602,4.250621 19.490909,3.50045 C 20.984007,3.188868 22.590966,2.996311 24.257151,2.930667 C 25.237216,2.945249 26.030543,2.962906 26.513532,3 z M 31.282791,3.584203 C 31.750192,3.63644 32.579862,3.801848 33.795668,4.099073 C 35.16683,4.536772 36.347892,5.070843 37.300167,5.681966 C 38.791373,6.941773 38.682595,7.174131 38.326732,7.242338 L 29.958044,8.273031 C 29.523357,8.326279 29.110469,8.223798 28.640151,8.037648 C 28.310696,7.84668 28.145983,7.666425 28.251072,7.487131 L 30.491474,3.664751 C 30.569914,3.579221 30.74289,3.523866 31.282791,3.584203 z M 14.624127,5.566491 C 14.672763,5.573403 14.726595,5.584742 14.766964,5.598777 L 20.895251,8.390464 C 21.198262,8.528985 21.220027,8.735063 21.123149,8.995188 C 20.922542,9.242607 20.678301,9.416099 20.26438,9.466802 L 11.903779,10.483696 C 11.524573,10.505763 11.159907,10.326253 11.066412,8.93171 C 11.140424,8.558069 11.274477,8.190349 11.494076,7.81569 C 11.713674,7.441031 11.999766,7.091935 12.344331,6.751435 C 13.737443,5.722497 14.306188,5.521308 14.624127,5.566491 z M 39.255235,8.583639 C 39.579997,8.617182 39.852498,8.921419 39.933588,10.130937 C 39.859576,10.504578 39.725523,10.872298 39.505924,11.246957 C 39.286326,11.621617 39.000234,11.970712 38.655669,12.311212 C 37.04945,13.49755 36.537303,13.569656 36.233036,13.46387 L 30.104749,10.672183 C 29.81764,10.540931 29.776139,10.349545 29.852587,10.108857 C 29.862751,10.095409 29.866324,10.080443 29.876851,10.067459 C 29.881935,10.053808 29.895209,10.040031 29.901116,10.026061 C 30.098159,9.800145 30.343421,9.643887 30.73562,9.595845 L 39.096221,8.578951 C 39.146533,8.576023 39.205556,8.578508 39.255235,8.583639 z M 21.647827,10.804671 C 21.852925,10.839608 22.06662,10.90434 22.28547,10.986308 C 22.662017,11.191948 22.861024,11.384269 22.748928,11.575516 L 20.508526,15.397896 C 20.362179,15.557471 19.824526,15.604125 17.204332,14.963574 C 15.833169,14.525878 14.652109,13.991803 13.699833,13.380681 C 12.20863,12.120874 12.317406,11.888514 12.673268,11.820309 L 21.041956,10.789616 C 21.246227,10.764594 21.442729,10.769735 21.647827,10.804671 z M 27.473567,11.518286 C 27.677762,11.536323 27.840994,11.574411 27.984548,11.640038 L 34.104747,14.445524 C 34.338335,14.585932 34.1784,14.812025 31.509091,15.562197 C 30.015994,15.873778 28.409033,16.066338 26.742849,16.13198 C 23.773106,16.0878 23.358511,15.924502 23.406303,15.752855 L 25.646705,11.930475 C 25.763112,11.731871 26.173739,11.612847 26.760965,11.519736 C 27.024213,11.502263 27.269373,11.500249 27.473567,11.518286 z "
+ id="path2992"
+ sodipodi:nodetypes="csssccccccccccccccccccccccccccsccccscccsssccccccccccccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient5743);stroke-width:0.82348359;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3882"
+ sodipodi:cx="85.3125"
+ sodipodi:cy="10.8125"
+ sodipodi:rx="14.875"
+ sodipodi:ry="5.8125"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ transform="matrix(1.142856,0,0,1.290324,-71.99989,-3.951621)" />
+ <path
+ style="opacity:0.7;color:black;fill:url(#linearGradient3003);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 24.21875,2.46875 C 22.529463,2.535304 20.928347,2.713616 19.40625,3.03125 C 19.395836,3.030905 19.385414,3.030905 19.375,3.03125 C 18.030353,3.409144 17.31816,3.671511 16.90625,3.875 C 16.700295,3.976745 16.533745,4.024698 16.40625,4.3125 C 16.342503,4.456401 16.359638,4.683059 16.4375,4.8125 C 16.515362,4.941941 16.592126,4.992705 16.65625,5.03125 C 16.676283,5.043196 16.697173,5.053642 16.71875,5.0625 L 22.8125,7.875 C 23.277073,8.087382 23.766882,8.043314 24.34375,8 C 24.354164,8.000345 24.364586,8.000345 24.375,8 C 24.66736,7.951176 24.93252,7.897112 25.15625,7.8125 C 25.37998,7.727888 25.602277,7.627033 25.75,7.375 L 28,3.5625 C 28.02692,3.524165 28.047984,3.482037 28.0625,3.4375 C 28.10103,3.299111 28.051103,3.040171 27.9375,2.90625 C 27.823897,2.772329 27.713327,2.729453 27.59375,2.6875 C 27.354596,2.603594 27.039179,2.57026 26.53125,2.53125 C 26.016219,2.491695 25.229781,2.483328 24.25,2.46875 C 24.239586,2.468405 24.229164,2.468405 24.21875,2.46875 z M 24.25,2.9375 C 25.230065,2.952082 26.017011,2.962906 26.5,3 C 27.480535,3.075308 27.625768,3.197501 27.59375,3.3125 L 25.34375,7.125 C 25.231655,7.316248 24.863514,7.43923 24.3125,7.53125 C 23.754142,7.573174 23.299072,7.574222 23,7.4375 L 16.90625,4.625 C 16.672665,4.484589 16.830693,4.250171 19.5,3.5 C 20.993097,3.188418 22.583815,3.003144 24.25,2.9375 z M 30.65625,3.09375 C 30.474425,3.112971 30.282388,3.206211 30.15625,3.34375 C 30.133508,3.362583 30.112583,3.383508 30.09375,3.40625 L 27.84375,7.25 C 27.710285,7.477708 27.766317,7.846965 27.90625,8.03125 C 28.046183,8.215535 28.187487,8.328808 28.375,8.4375 C 28.395033,8.449446 28.415923,8.459892 28.4375,8.46875 C 28.938239,8.66694 29.475613,8.818064 30.03125,8.75 L 38.375,7.71875 C 38.385414,7.719095 38.395836,7.719095 38.40625,7.71875 C 38.538892,7.693327 38.708411,7.654058 38.875,7.4375 C 39.041589,7.220942 39.030807,6.912667 38.9375,6.71875 C 38.750886,6.330917 38.38701,5.956264 37.625,5.3125 C 37.604967,5.300554 37.584077,5.290108 37.5625,5.28125 C 36.56488,4.641027 35.340283,4.104044 33.9375,3.65625 C 33.927577,3.645351 33.917149,3.634923 33.90625,3.625 C 32.678785,3.324925 31.850309,3.181613 31.34375,3.125 C 31.057919,3.093057 30.838075,3.074529 30.65625,3.09375 z M 30.71875,3.5625 C 30.839287,3.545512 31.0113,3.563581 31.28125,3.59375 C 31.748651,3.645987 32.565444,3.796525 33.78125,4.09375 C 35.15241,4.531449 36.360225,5.076377 37.3125,5.6875 C 38.803706,6.947307 38.668363,7.181793 38.3125,7.25 L 29.96875,8.28125 C 29.534063,8.334498 29.095318,8.2174 28.625,8.03125 C 28.295546,7.840282 28.144911,7.679294 28.25,7.5 L 30.5,3.65625 C 30.53922,3.613485 30.598213,3.579488 30.71875,3.5625 z M 14.6875,5.09375 C 14.096019,5.009693 13.481171,5.327185 12.0625,6.375 C 12.040923,6.383858 12.020033,6.394304 12,6.40625 C 11.632082,6.769827 11.337374,7.14685 11.09375,7.5625 C 10.849839,7.978638 10.67867,8.415039 10.59375,8.84375 C 10.588209,8.885233 10.588209,8.927267 10.59375,8.96875 C 10.643206,9.706413 10.75471,10.158753 11,10.5 C 11.24529,10.841247 11.673435,10.952867 11.9375,10.9375 C 11.947914,10.937845 11.958336,10.937845 11.96875,10.9375 L 20.3125,9.9375 C 20.829557,9.874163 21.240367,9.632717 21.5,9.3125 C 21.530023,9.26458 21.551193,9.211656 21.5625,9.15625 C 21.628907,8.977942 21.693002,8.781478 21.625,8.53125 C 21.556998,8.281022 21.331424,8.046152 21.09375,7.9375 L 14.96875,5.15625 C 14.958336,5.155905 14.947914,5.155905 14.9375,5.15625 C 14.811235,5.112352 14.740824,5.101328 14.6875,5.09375 z M 14.625,5.5625 C 14.673636,5.569412 14.740881,5.579715 14.78125,5.59375 L 20.90625,8.375 C 21.209262,8.513521 21.221878,8.739875 21.125,9 C 20.924392,9.247419 20.663921,9.418047 20.25,9.46875 L 11.90625,10.46875 C 11.527044,10.490817 11.155995,10.332043 11.0625,8.9375 C 11.136512,8.563859 11.280401,8.187159 11.5,7.8125 C 11.719598,7.437841 11.999185,7.0905 12.34375,6.75 C 13.736862,5.721062 14.307061,5.517317 14.625,5.5625 z M 39.03125,8.125 L 30.6875,9.125 C 30.227736,9.181318 29.879783,9.392513 29.625,9.65625 C 29.623983,9.656939 29.598616,9.684777 29.59375,9.6875 C 29.584051,9.698064 29.571837,9.708045 29.5625,9.71875 C 29.551601,9.728673 29.541173,9.739101 29.53125,9.75 L 29.5,9.75 C 29.550744,9.687412 29.541034,9.716862 29.46875,9.8125 C 29.468405,9.822914 29.468405,9.833336 29.46875,9.84375 C 29.444249,9.872393 29.423266,9.903867 29.40625,9.9375 C 29.34829,10.119981 29.292723,10.361835 29.375,10.59375 C 29.457277,10.825665 29.678664,11.020959 29.90625,11.125 L 36.03125,13.90625 C 36.041664,13.906595 36.052086,13.906595 36.0625,13.90625 C 36.327468,13.998373 36.691653,13.992584 37.125,13.8125 C 37.558347,13.632416 38.118404,13.292476 38.9375,12.6875 C 38.959077,12.678642 38.979967,12.668196 39,12.65625 C 39.367917,12.292673 39.662626,11.915652 39.90625,11.5 C 40.150161,11.083863 40.321331,10.647464 40.40625,10.21875 C 40.411791,10.177268 40.411791,10.135232 40.40625,10.09375 C 40.363432,9.455094 40.270005,9.018395 40.09375,8.6875 C 39.917495,8.356605 39.598432,8.154532 39.3125,8.125 C 39.212859,8.114709 39.119476,8.121684 39.0625,8.125 C 39.052086,8.124655 39.041664,8.124655 39.03125,8.125 z M 39.09375,8.59375 C 39.144061,8.590822 39.200321,8.588619 39.25,8.59375 C 39.574763,8.627293 39.85641,8.915482 39.9375,10.125 C 39.86349,10.498641 39.719599,10.875341 39.5,11.25 C 39.280402,11.62466 39.000815,11.972 38.65625,12.3125 C 37.05003,13.498838 36.523017,13.574536 36.21875,13.46875 L 30.09375,10.6875 C 29.80664,10.556248 29.767302,10.334438 29.84375,10.09375 C 29.853914,10.080302 29.864473,10.075484 29.875,10.0625 C 29.880085,10.048849 29.900343,10.04522 29.90625,10.03125 C 30.103294,9.805334 30.357801,9.641792 30.75,9.59375 L 39.09375,8.59375 z M 20.96875,10.3125 L 12.625,11.34375 C 12.614586,11.343405 12.604164,11.343405 12.59375,11.34375 C 12.461108,11.369172 12.291589,11.408441 12.125,11.625 C 11.958411,11.841559 11.969193,12.149834 12.0625,12.34375 C 12.249114,12.731583 12.612992,13.106236 13.375,13.75 C 13.395033,13.761946 13.415923,13.772392 13.4375,13.78125 C 14.435119,14.421471 15.659713,14.958459 17.0625,15.40625 C 17.072423,15.417149 17.082851,15.427577 17.09375,15.4375 C 18.413166,15.760054 19.231179,15.914896 19.75,15.96875 C 20.00941,15.995677 20.199598,15.995126 20.375,15.96875 C 20.550402,15.942374 20.721219,15.852356 20.84375,15.71875 C 20.866492,15.699917 20.887417,15.678992 20.90625,15.65625 L 23.15625,11.8125 C 23.230934,11.685081 23.282536,11.503864 23.25,11.34375 C 23.217464,11.183636 23.110915,11.054933 23.03125,10.96875 C 22.87192,10.796385 22.70354,10.704907 22.5,10.59375 C 22.479967,10.581804 22.459077,10.571358 22.4375,10.5625 C 22.213091,10.47845 22.008525,10.387788 21.75,10.34375 C 21.522274,10.30496 21.244957,10.278666 20.96875,10.3125 z M 21.03125,10.78125 C 21.235521,10.756228 21.451152,10.777564 21.65625,10.8125 C 21.861348,10.847437 22.0624,10.918032 22.28125,11 C 22.657798,11.20564 22.862096,11.371253 22.75,11.5625 L 20.5,15.40625 C 20.353653,15.565825 19.838944,15.609301 17.21875,14.96875 C 15.847587,14.531054 14.639776,13.986122 13.6875,13.375 C 12.196297,12.115193 12.331638,11.880705 12.6875,11.8125 L 21.03125,10.78125 z M 26.6875,11.0625 C 26.380288,11.111212 26.108719,11.167973 25.875,11.25 C 25.641281,11.332027 25.404512,11.423882 25.25,11.6875 L 23,15.5 C 22.97308,15.538335 22.952016,15.580463 22.9375,15.625 C 22.904935,15.741959 22.920591,15.932646 23,16.0625 C 23.079409,16.192354 23.19427,16.237608 23.28125,16.28125 C 23.45521,16.368533 23.617124,16.400918 23.875,16.4375 C 24.390753,16.510664 25.25703,16.571539 26.75,16.59375 C 26.760414,16.594095 26.770836,16.594095 26.78125,16.59375 C 28.470537,16.527198 30.071654,16.348883 31.59375,16.03125 C 31.604164,16.031595 31.614586,16.031595 31.625,16.03125 C 32.969648,15.653356 33.681841,15.39099 34.09375,15.1875 C 34.299705,15.085755 34.466255,15.037804 34.59375,14.75 C 34.657497,14.606098 34.640364,14.37944 34.5625,14.25 C 34.484636,14.12056 34.407874,14.069795 34.34375,14.03125 C 34.323717,14.019304 34.302827,14.008858 34.28125,14 L 28.1875,11.1875 C 27.922241,11.066235 27.694869,11.079713 27.5,11.0625 C 27.261132,11.0414 27.001137,11.043757 26.71875,11.0625 C 26.708336,11.062155 26.697914,11.062155 26.6875,11.0625 z M 26.75,11.53125 C 27.013248,11.513777 27.264556,11.513213 27.46875,11.53125 C 27.672946,11.549287 27.856446,11.559373 28,11.625 L 34.09375,14.4375 C 34.327337,14.577908 34.169309,14.812328 31.5,15.5625 C 30.006904,15.874081 28.416184,16.059358 26.75,16.125 C 23.780257,16.080819 23.358458,15.921647 23.40625,15.75 L 25.65625,11.9375 C 25.772656,11.738896 26.162774,11.624361 26.75,11.53125 z "
+ id="path6550" />
+ <path
+ style="opacity:0.29714286;fill:url(#linearGradient3005);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 32.75,28.125 C 36.665221,27.989041 39.663593,27.509264 42.25,26.875 L 44.6875,34.8125 C 41.143944,37.348492 36.537218,37.994406 31.5,37.875 L 32.75,28.125 z "
+ id="path2111"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:url(#linearGradient3017);fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1.0;opacity:0.6"
+ d="M 41.100582,23.560622 C 42.545852,23.922695 43.601874,24.362619 44.459339,24.842253 C 45.66266,23.973241 47.054739,21.640304 44.724504,18.831845 C 44.97435,19.937288 45.182433,21.455139 41.100582,23.560622 z "
+ id="path3009"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="troiai2"
+ style="display:none">
+ <path
+ style="opacity:1;color:black;fill:url(#linearGradient4796);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -19.970667,8.274237 C -29.301506,7.13127 -38.90351,9.665589 -41.403702,13.931199 C -43.903895,18.196811 -38.360174,22.58638 -29.029333,23.729347 C -19.698494,24.872313 -10.096492,22.337996 -7.596298,18.072384 C -5.096106,13.806773 -10.639827,9.417203 -19.970667,8.274237 z M -23.486468,9.470468 C -22.505932,9.545776 -22.374285,9.665262 -22.406303,9.780261 L -24.646705,13.60264 C -24.758801,13.793888 -25.141492,13.914954 -25.692506,14.006974 C -26.250865,14.048898 -26.685476,14.0298 -26.984548,13.893078 L -33.104747,11.087592 C -33.338333,10.947181 -33.178398,10.721089 -30.509091,9.970918 C -29.015993,9.659336 -27.409034,9.466779 -25.742849,9.401135 C -24.762784,9.415717 -23.969457,9.433374 -23.486468,9.470468 z M -18.717209,10.054671 C -18.249808,10.106908 -17.420138,10.272316 -16.204332,10.569541 C -14.83317,11.00724 -13.652108,11.541311 -12.699833,12.152434 C -11.208627,13.412241 -11.317405,13.644599 -11.673268,13.712806 L -20.041956,14.743499 C -20.476643,14.796747 -20.889531,14.694266 -21.359849,14.508116 C -21.689304,14.317148 -21.854017,14.136893 -21.748928,13.957599 L -19.508526,10.135219 C -19.430086,10.049689 -19.25711,9.994334 -18.717209,10.054671 z M -35.375873,12.036959 C -35.327237,12.043871 -35.273405,12.05521 -35.233036,12.069245 L -29.104749,14.860932 C -28.801738,14.999453 -28.779973,15.205531 -28.876851,15.465656 C -29.077458,15.713075 -29.321699,15.886567 -29.73562,15.93727 L -38.096221,16.954164 C -38.475427,16.976231 -38.840093,16.796721 -38.933588,15.402178 C -38.859576,15.028537 -38.725523,14.660817 -38.505924,14.286158 C -38.286326,13.911499 -38.000234,13.562403 -37.655669,13.221903 C -36.262557,12.192965 -35.693812,11.991776 -35.375873,12.036959 z M -10.744765,15.054107 C -10.420003,15.08765 -10.147502,15.391887 -10.066412,16.601405 C -10.140424,16.975046 -10.274477,17.342766 -10.494076,17.717425 C -10.713674,18.092085 -10.999766,18.44118 -11.344331,18.78168 C -12.95055,19.968018 -13.462697,20.040124 -13.766964,19.934338 L -19.895251,17.142651 C -20.18236,17.011399 -20.223861,16.820013 -20.147413,16.579325 C -20.137249,16.565877 -20.133676,16.550911 -20.123149,16.537927 C -20.118065,16.524276 -20.104791,16.510499 -20.098884,16.496529 C -19.901841,16.270613 -19.656579,16.114355 -19.26438,16.066313 L -10.903779,15.049419 C -10.853467,15.046491 -10.794444,15.048976 -10.744765,15.054107 z M -28.352173,17.275139 C -28.147075,17.310076 -27.93338,17.374808 -27.71453,17.456776 C -27.337983,17.662416 -27.138976,17.854737 -27.251072,18.045984 L -29.491474,21.868364 C -29.637821,22.027939 -30.175474,22.074593 -32.795668,21.434042 C -34.166831,20.996346 -35.347891,20.462271 -36.300167,19.851149 C -37.79137,18.591342 -37.682594,18.358982 -37.326732,18.290777 L -28.958044,17.260084 C -28.753773,17.235062 -28.557271,17.240203 -28.352173,17.275139 z M -22.526433,17.988754 C -22.322238,18.006791 -22.159006,18.044879 -22.015452,18.110506 L -15.895253,20.915992 C -15.661665,21.0564 -15.8216,21.282493 -18.490909,22.032665 C -19.984006,22.344246 -21.590967,22.536806 -23.257151,22.602448 C -26.226894,22.558268 -26.641489,22.39497 -26.593697,22.223323 L -24.353295,18.400943 C -24.236888,18.202339 -23.826261,18.083315 -23.239035,17.990204 C -22.975787,17.972731 -22.730627,17.970717 -22.526433,17.988754 z "
+ id="path3888" />
+ <g
+ style="display:inline"
+ id="g2992">
+ <g
+ style="stroke:#2e3436"
+ id="g3891">
+ <path
+ style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -23.486468,9.470468 C -22.505932,9.545776 -22.374285,9.665262 -22.406303,9.780261 L -24.646705,13.60264 C -24.758801,13.793888 -25.141492,13.914954 -25.692506,14.006974 C -26.250865,14.048898 -26.685476,14.0298 -26.984548,13.893078 L -33.104747,11.087592 C -33.338333,10.947181 -33.178398,10.721089 -30.509091,9.970918 C -29.015993,9.659336 -27.409034,9.466779 -25.742849,9.401135 C -24.762784,9.415717 -23.969457,9.433374 -23.486468,9.470468 z M -18.717209,10.054671 C -18.249808,10.106908 -17.420138,10.272316 -16.204332,10.569541 C -14.83317,11.00724 -13.652108,11.541311 -12.699833,12.152434 C -11.208627,13.412241 -11.317405,13.644599 -11.673268,13.712806 L -20.041956,14.743499 C -20.476643,14.796747 -20.889531,14.694266 -21.359849,14.508116 C -21.689304,14.317148 -21.854017,14.136893 -21.748928,13.957599 L -19.508526,10.135219 C -19.430086,10.049689 -19.25711,9.994334 -18.717209,10.054671 z M -35.375873,12.036959 C -35.327237,12.043871 -35.273405,12.05521 -35.233036,12.069245 L -29.104749,14.860932 C -28.801738,14.999453 -28.779973,15.205531 -28.876851,15.465656 C -29.077458,15.713075 -29.321699,15.886567 -29.73562,15.93727 L -38.096221,16.954164 C -38.475427,16.976231 -38.840093,16.796721 -38.933588,15.402178 C -38.859576,15.028537 -38.725523,14.660817 -38.505924,14.286158 C -38.286326,13.911499 -38.000234,13.562403 -37.655669,13.221903 C -36.262557,12.192965 -35.693812,11.991776 -35.375873,12.036959 z M -10.744765,15.054107 C -10.420003,15.08765 -10.147502,15.391887 -10.066412,16.601405 C -10.140424,16.975046 -10.274477,17.342766 -10.494076,17.717425 C -10.713674,18.092085 -10.999766,18.44118 -11.344331,18.78168 C -12.95055,19.968018 -13.462697,20.040124 -13.766964,19.934338 L -19.895251,17.142651 C -20.18236,17.011399 -20.223861,16.820013 -20.147413,16.579325 C -20.137249,16.565877 -20.133676,16.550911 -20.123149,16.537927 C -20.118065,16.524276 -20.104791,16.510499 -20.098884,16.496529 C -19.901841,16.270613 -19.656579,16.114355 -19.26438,16.066313 L -10.903779,15.049419 C -10.853467,15.046491 -10.794444,15.048976 -10.744765,15.054107 z M -28.352173,17.275139 C -28.147075,17.310076 -27.93338,17.374808 -27.71453,17.456776 C -27.337983,17.662416 -27.138976,17.854737 -27.251072,18.045984 L -29.491474,21.868364 C -29.637821,22.027939 -30.175474,22.074593 -32.795668,21.434042 C -34.166831,20.996346 -35.347891,20.462271 -36.300167,19.851149 C -37.79137,18.591342 -37.682594,18.358982 -37.326732,18.290777 L -28.958044,17.260084 C -28.753773,17.235062 -28.557271,17.240203 -28.352173,17.275139 z M -22.526433,17.988754 C -22.322238,18.006791 -22.159006,18.044879 -22.015452,18.110506 L -15.895253,20.915992 C -15.661665,21.0564 -15.8216,21.282493 -18.490909,22.032665 C -19.984006,22.344246 -21.590967,22.536806 -23.257151,22.602448 C -26.226894,22.558268 -26.641489,22.39497 -26.593697,22.223323 L -24.353295,18.400943 C -24.236888,18.202339 -23.826261,18.083315 -23.239035,17.990204 C -22.975787,17.972731 -22.730627,17.970717 -22.526433,17.988754 z "
+ id="path3884"
+ sodipodi:nodetypes="cccccccccccccccccccccccccsccccscccsssccccccccccccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:0.75173426;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3886"
+ sodipodi:cx="85.3125"
+ sodipodi:cy="10.8125"
+ sodipodi:rx="14.875"
+ sodipodi:ry="5.8125"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ transform="matrix(1.210083,0,0,1.462367,-127.7352,0.188166)" />
+ </g>
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.90075187;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3903);stroke-width:0.82348335;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3895"
+ sodipodi:cx="85.3125"
+ sodipodi:cy="10.8125"
+ sodipodi:rx="14.875"
+ sodipodi:ry="5.8125"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ transform="matrix(1.142857,0,0,1.290324,-122,2.04838)" />
+ </g>
+ <g
+ style="display:inline"
+ id="g3189"
+ transform="translate(50,0)">
+ <path
+ id="path3152"
+ d="M 25.96875,8.002653 C 19.762261,7.929462 13.664379,9.373241 10.34375,12.00219 C 5.513746,15.826114 8.384206,20.721752 16.75,22.929496 C 25.115794,25.137239 35.826245,23.825188 40.65625,20.001263 C 45.48625,16.177339 42.61579,11.281701 34.25,9.073958 C 31.635689,8.384038 28.789881,8.035922 25.96875,8.002653 z M 29.6875,10.388091 C 30.5522,10.360109 31.443298,10.446598 32.25,10.659488 C 34.401203,11.227194 35.148253,12.490153 33.90625,13.473448 C 32.664247,14.456743 29.901206,14.783926 27.75,14.216219 C 25.598795,13.648513 24.851747,12.399839 26.09375,11.416543 C 26.87,10.801984 28.246333,10.434729 29.6875,10.388091 z M 17.40625,11.887917 C 18.27095,11.859935 19.162048,11.946425 19.96875,12.159315 C 22.119954,12.727021 22.835753,13.989977 21.59375,14.973274 C 20.351748,15.956572 17.619956,16.298036 15.46875,15.730329 C 13.317545,15.162623 12.570497,13.899667 13.8125,12.91637 C 14.588751,12.301809 15.965083,11.934556 17.40625,11.887917 z M 32.96875,16.001727 C 33.83345,15.973744 34.724548,16.060234 35.53125,16.273124 C 37.682456,16.84083 38.429503,18.103786 37.1875,19.087083 C 35.9455,20.070381 33.182456,20.411845 31.03125,19.844139 C 28.880048,19.276432 28.164247,18.013476 29.40625,17.030179 C 30.182502,16.415618 31.527584,16.048365 32.96875,16.001727 z M 20.6875,17.515837 C 21.5522,17.487854 22.443298,17.574344 23.25,17.787234 C 25.401203,18.35494 26.148253,19.603614 24.90625,20.58691 C 23.664247,21.570205 20.901206,21.911671 18.75,21.343965 C 16.598795,20.776258 15.851747,19.5133 17.09375,18.530005 C 17.87,17.915446 19.246333,17.562474 20.6875,17.515837 z "
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#555753;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <path
+ id="path2959"
+ d="M 25.96875,8.002653 C 19.762261,7.929462 13.664379,9.373241 10.34375,12.00219 C 5.513746,15.826114 8.384206,20.721752 16.75,22.929496 C 25.115794,25.137239 35.826245,23.825188 40.65625,20.001263 C 45.48625,16.177339 42.61579,11.281701 34.25,9.073958 C 31.635689,8.384038 28.789881,8.035922 25.96875,8.002653 z M 29.6875,10.388091 C 30.5522,10.360109 31.443298,10.446598 32.25,10.659488 C 34.401203,11.227194 35.148253,12.490153 33.90625,13.473448 C 32.664247,14.456743 29.901206,14.783926 27.75,14.216219 C 25.598795,13.648513 24.851747,12.399839 26.09375,11.416543 C 26.87,10.801984 28.246333,10.434729 29.6875,10.388091 z M 17.40625,11.887917 C 18.27095,11.859935 19.162048,11.946425 19.96875,12.159315 C 22.119954,12.727021 22.835753,13.989977 21.59375,14.973274 C 20.351748,15.956572 17.619956,16.298036 15.46875,15.730329 C 13.317545,15.162623 12.570497,13.899667 13.8125,12.91637 C 14.588751,12.301809 15.965083,11.934556 17.40625,11.887917 z M 32.96875,16.001727 C 33.83345,15.973744 34.724548,16.060234 35.53125,16.273124 C 37.682456,16.84083 38.429503,18.103786 37.1875,19.087083 C 35.9455,20.070381 33.182456,20.411845 31.03125,19.844139 C 28.880048,19.276432 28.164247,18.013476 29.40625,17.030179 C 30.182502,16.415618 31.527584,16.048365 32.96875,16.001727 z M 20.6875,17.515837 C 21.5522,17.487854 22.443298,17.574344 23.25,17.787234 C 25.401203,18.35494 26.148253,19.603614 24.90625,20.58691 C 23.664247,21.570205 20.901206,21.911671 18.75,21.343965 C 16.598795,20.776258 15.851747,19.5133 17.09375,18.530005 C 17.87,17.915446 19.246333,17.562474 20.6875,17.515837 z "
+ style="opacity:1;color:black;fill:url(#linearGradient3072);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <path
+ transform="matrix(1.142857,0,0,1.290323,-72.00001,2.048386)"
+ d="M 100.1875 10.8125 A 14.875 5.8125 0 1 1 70.4375,10.8125 A 14.875 5.8125 0 1 1 100.1875 10.8125 z"
+ sodipodi:ry="5.8125"
+ sodipodi:rx="14.875"
+ sodipodi:cy="10.8125"
+ sodipodi:cx="85.3125"
+ id="path2968"
+ style="opacity:0.6;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3215);stroke-width:0.82348335;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -67.21875,4.0285714 C -66.228989,3.9852983 -66.03418,4.0851337 -66,4.2 L -66,7.7 C -66,7.8856183 -66.261153,8.0402377 -66.6875,8.1857143 C -66.734238,8.2040411 -66.791816,8.2143269 -66.84375,8.2285714 C -67.33252,8.3246217 -67.742113,8.349952 -68.09375,8.2571429 L -74.75,6.5 C -75.039409,6.3975771 -74.96304,6.16496 -73.125,5.2571429 C -73.114525,5.251968 -73.104347,5.2480777 -73.09375,5.2428571 C -71.886156,4.7872091 -70.474594,4.4285714 -68.96875,4.1857143 C -68.265233,4.1158903 -67.618098,4.0460297 -67.21875,4.0285714 z M -62.28125,4.0285714 C -61.862086,4.0243474 -61.083492,4.08128 -60.03125,4.1857143 C -58.525406,4.4285714 -57.113844,4.7872091 -55.90625,5.2428571 C -55.894767,5.2471909 -55.886451,5.2527909 -55.875,5.2571429 C -54.03696,6.16496 -53.960591,6.3975771 -54.25,6.5 L -60.90625,8.2571429 C -61.29598,8.3600091 -61.752818,8.3098697 -62.3125,8.1857143 C -62.738847,8.0402377 -63,7.8856137 -63,7.7 L -63,4.2 C -62.972656,4.1081051 -62.836914,4.0341714 -62.28125,4.0285714 z M -76.40625,7.6857143 C -76.356212,7.6866377 -76.29694,7.6912183 -76.25,7.7 L -69.59375,9.4428571 C -69.242114,9.5356617 -69.093676,9.715616 -69.03125,9.9571429 C -69.031344,9.9857006 -69.03125,10.014299 -69.03125,10.042857 C -69.093675,10.284384 -69.242113,10.464334 -69.59375,10.557143 L -76.25,12.3 C -76.583792,12.362437 -77.002186,12.230194 -77.78125,11.1 C -77.922377,10.742473 -78,10.376023 -78,10 C -78,9.6239771 -77.922377,9.2575269 -77.78125,8.9 C -77.111743,7.9287406 -76.71203,7.6800823 -76.40625,7.6857143 z M -52.59375,7.6857143 C -52.28797,7.6800823 -51.888257,7.9287406 -51.21875,8.9 C -51.077623,9.2575269 -51,9.6239771 -51,10 C -51,10.376023 -51.077623,10.742473 -51.21875,11.1 C -51.997814,12.230194 -52.416209,12.362437 -52.75,12.3 L -59.40625,10.557143 C -59.757886,10.464338 -59.906324,10.284384 -59.96875,10.042857 C -59.969649,10.014277 -59.96875,9.9857234 -59.96875,9.9571429 C -59.906325,9.715616 -59.757887,9.5356663 -59.40625,9.4428571 L -52.75,7.7 C -52.70306,7.6912183 -52.643788,7.6866377 -52.59375,7.6857143 z M -67.5,11.685714 C -67.282111,11.695195 -67.039049,11.732434 -66.78125,11.785714 C -66.301109,11.939794 -66,12.102011 -66,12.3 L -66,15.8 C -66.047971,15.961216 -66.521607,16.057161 -68.96875,15.814286 C -70.474594,15.571429 -71.886156,15.212791 -73.09375,14.757143 C -73.105233,14.752809 -73.113549,14.747209 -73.125,14.742857 C -74.96304,13.83504 -75.039409,13.602423 -74.75,13.5 L -68.09375,11.742857 C -67.910606,11.694519 -67.717889,11.676233 -67.5,11.685714 z M -61.46875,11.685714 C -61.261301,11.678976 -61.082069,11.696453 -60.90625,11.742857 L -54.25,13.5 C -53.960592,13.602423 -54.036961,13.83504 -55.875,14.742857 C -55.886451,14.747209 -55.894767,14.752809 -55.90625,14.757143 C -57.113845,15.212791 -58.525406,15.571429 -60.03125,15.814286 C -62.478393,16.057161 -62.952029,15.961216 -63,15.8 L -63,12.3 C -63,12.114382 -62.738847,11.959762 -62.3125,11.814286 C -62.266586,11.798619 -62.206271,11.786926 -62.15625,11.771429 C -61.911865,11.723406 -61.676199,11.692453 -61.46875,11.685714 z "
+ id="path5719"
+ sodipodi:nodetypes="cccssccssccccsscccccccccsscccsccccscccssccccccccssccccccsscccssc" />
+ <path
+ style="color:black;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -64.5,2 C -74.159996,2 -82,5.584 -82,10 C -82,14.416 -74.159997,18.000005 -64.5,18 C -54.840004,18 -47.000001,14.416 -47,10 C -47,5.584 -54.840004,2 -64.5,2 z M -67.21875,4.0285714 C -66.228989,3.9852983 -66.03418,4.0851337 -66,4.2 L -66,7.7 C -66,7.8856183 -66.261153,8.0402377 -66.6875,8.1857143 C -66.734238,8.2040411 -66.791816,8.2143269 -66.84375,8.2285714 C -67.33252,8.3246217 -67.742113,8.349952 -68.09375,8.2571429 L -74.75,6.5 C -75.039409,6.3975771 -74.96304,6.16496 -73.125,5.2571429 C -73.114525,5.251968 -73.104347,5.2480777 -73.09375,5.2428571 C -71.886156,4.7872091 -70.474594,4.4285714 -68.96875,4.1857143 C -68.265233,4.1158903 -67.618098,4.0460297 -67.21875,4.0285714 z M -62.28125,4.0285714 C -61.862086,4.0243474 -61.083492,4.08128 -60.03125,4.1857143 C -58.525406,4.4285714 -57.113844,4.7872091 -55.90625,5.2428571 C -55.894767,5.2471909 -55.886451,5.2527909 -55.875,5.2571429 C -54.03696,6.16496 -53.960591,6.3975771 -54.25,6.5 L -60.90625,8.2571429 C -61.29598,8.3600091 -61.752818,8.3098697 -62.3125,8.1857143 C -62.738847,8.0402377 -63,7.8856137 -63,7.7 L -63,4.2 C -62.972656,4.1081051 -62.836914,4.0341714 -62.28125,4.0285714 z M -76.40625,7.6857143 C -76.356212,7.6866377 -76.29694,7.6912183 -76.25,7.7 L -69.59375,9.4428571 C -69.242114,9.5356617 -69.093676,9.715616 -69.03125,9.9571429 C -69.031344,9.9857006 -69.03125,10.014299 -69.03125,10.042857 C -69.093675,10.284384 -69.242113,10.464334 -69.59375,10.557143 L -76.25,12.3 C -76.583792,12.362437 -77.002186,12.230194 -77.78125,11.1 C -77.922377,10.742473 -78,10.376023 -78,10 C -78,9.6239771 -77.922377,9.2575269 -77.78125,8.9 C -77.111743,7.9287406 -76.71203,7.6800823 -76.40625,7.6857143 z M -52.59375,7.6857143 C -52.28797,7.6800823 -51.888257,7.9287406 -51.21875,8.9 C -51.077623,9.2575269 -51,9.6239771 -51,10 C -51,10.376023 -51.077623,10.742473 -51.21875,11.1 C -51.997814,12.230194 -52.416209,12.362437 -52.75,12.3 L -59.40625,10.557143 C -59.757886,10.464338 -59.906324,10.284384 -59.96875,10.042857 C -59.969649,10.014277 -59.96875,9.9857234 -59.96875,9.9571429 C -59.906325,9.715616 -59.757887,9.5356663 -59.40625,9.4428571 L -52.75,7.7 C -52.70306,7.6912183 -52.643788,7.6866377 -52.59375,7.6857143 z M -67.5,11.685714 C -67.282111,11.695195 -67.039049,11.732434 -66.78125,11.785714 C -66.301109,11.939794 -66,12.102011 -66,12.3 L -66,15.8 C -66.047971,15.961216 -66.521607,16.057161 -68.96875,15.814286 C -70.474594,15.571429 -71.886156,15.212791 -73.09375,14.757143 C -73.105233,14.752809 -73.113549,14.747209 -73.125,14.742857 C -74.96304,13.83504 -75.039409,13.602423 -74.75,13.5 L -68.09375,11.742857 C -67.910606,11.694519 -67.717889,11.676233 -67.5,11.685714 z M -61.46875,11.685714 C -61.261301,11.678976 -61.082069,11.696453 -60.90625,11.742857 L -54.25,13.5 C -53.960592,13.602423 -54.036961,13.83504 -55.875,14.742857 C -55.886451,14.747209 -55.894767,14.752809 -55.90625,14.757143 C -57.113845,15.212791 -58.525406,15.571429 -60.03125,15.814286 C -62.478393,16.057161 -62.952029,15.961216 -63,15.8 L -63,12.3 C -63,12.114382 -62.738847,11.959762 -62.3125,11.814286 C -62.266586,11.798619 -62.206271,11.786926 -62.15625,11.771429 C -61.911865,11.723406 -61.676199,11.692453 -61.46875,11.685714 z "
+ id="path5712"
+ sodipodi:nodetypes="csssccccssccssccccsscccccccccsscccsccccscccssccccccccssccccccsscccssc" />
+ <path
+ style="color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -23.745287,-14.087957 C -22.764752,-14.012649 -22.633104,-13.893164 -22.665122,-13.778165 L -24.646705,-10.39736 C -24.751796,-10.218063 -25.091591,-10.099609 -25.585775,-10.009532 C -25.641296,-9.997359 -25.702736,-9.9942358 -25.760965,-9.9866215 C -26.287461,-9.9516732 -26.697439,-9.9756686 -26.984548,-10.106922 L -32.419156,-12.591789 C -32.640716,-12.724967 -32.435249,-12.940626 -30.145863,-13.600051 C -30.132815,-13.60381 -30.120781,-13.606364 -30.107589,-13.610152 C -28.68317,-13.907401 -27.116658,-14.08681 -25.524626,-14.143226 C -24.805549,-14.127432 -24.140912,-14.118345 -23.745287,-14.087957 z M -18.976028,-13.503755 C -18.568755,-13.45824 -17.848925,-13.311123 -16.891664,-13.085744 C -15.574628,-12.672987 -14.414213,-12.159548 -13.505739,-11.576535 C -13.497101,-11.57099 -13.492239,-11.564597 -13.483642,-11.559038 C -12.222208,-10.464662 -12.280141,-10.230931 -12.617677,-10.166238 L -20.041956,-9.2565005 C -20.476646,-9.2032501 -20.889531,-9.3057348 -21.359849,-9.4918833 C -21.689305,-9.6828511 -21.854016,-9.8631088 -21.748928,-10.042401 L -19.767345,-13.423206 C -19.688905,-13.508737 -19.515929,-13.564091 -18.976028,-13.503755 z M -34.690283,-11.642422 C -34.642473,-11.635609 -34.587814,-11.624171 -34.547445,-11.610135 L -29.104749,-9.1390678 C -28.817638,-9.0078183 -28.776142,-8.8164294 -28.852587,-8.5757414 C -28.868846,-8.5481673 -28.884947,-8.5205319 -28.901116,-8.4929462 C -29.098158,-8.2670306 -29.34342,-8.1107727 -29.73562,-8.0627297 L -37.151812,-7.166791 C -37.50958,-7.145975 -37.838846,-7.323217 -37.951486,-8.5071006 C -37.885384,-8.8691505 -37.75289,-9.2323053 -37.539999,-9.5955223 C -37.327107,-9.9587393 -37.044657,-10.303526 -36.705919,-10.632179 C -35.50933,-11.491146 -34.982455,-11.684042 -34.690283,-11.642422 z M -11.689174,-8.8249375 C -11.390625,-8.7941981 -11.145314,-8.5067145 -11.048514,-7.489316 C -11.114616,-7.127266 -11.24711,-6.764111 -11.460001,-6.400894 C -11.672893,-6.037677 -11.955343,-5.692891 -12.294081,-5.364239 C -13.686477,-4.364712 -14.165487,-4.286478 -14.452555,-4.386282 L -19.895251,-6.857349 C -20.182362,-6.988598 -20.223858,-7.179988 -20.147413,-7.420675 C -20.1321,-7.448389 -20.115066,-7.475864 -20.098884,-7.503471 C -19.901842,-7.729386 -19.65658,-7.885644 -19.26438,-7.933687 L -11.848188,-8.829626 C -11.797876,-8.8325545 -11.73803,-8.8299661 -11.689174,-8.8249375 z M -28.352173,-6.724861 C -28.147076,-6.689922 -27.93338,-6.625193 -27.71453,-6.543224 C -27.337985,-6.337581 -27.138978,-6.145262 -27.251072,-5.954016 L -29.232655,-2.573211 C -29.370267,-2.42316 -29.882085,-2.386523 -32.108336,-2.910672 C -33.425372,-3.32343 -34.585787,-3.83687 -35.494261,-4.419882 C -35.502899,-4.425427 -35.507761,-4.43182 -35.516358,-4.437379 C -36.777792,-5.531755 -36.719859,-5.765486 -36.382323,-5.830178 L -28.958044,-6.739916 C -28.753773,-6.764939 -28.557269,-6.7598 -28.352173,-6.724861 z M -22.526433,-6.011246 C -22.322237,-5.99321 -22.159007,-5.955122 -22.015452,-5.889495 L -16.580844,-3.404627 C -16.359285,-3.271451 -16.564752,-3.055791 -18.854137,-2.396366 C -18.867662,-2.393517 -18.878865,-2.389092 -18.892411,-2.386264 C -20.316831,-2.089016 -21.883342,-1.909607 -23.475374,-1.853191 C -25.97664,-1.908132 -26.379817,-2.05685 -26.334878,-2.218252 L -24.353295,-5.599057 C -24.248204,-5.778354 -23.908409,-5.896808 -23.414225,-5.986885 C -23.361006,-5.996586 -23.296126,-6.000744 -23.239035,-6.009795 C -22.975788,-6.027268 -22.730628,-6.029283 -22.526433,-6.011246 z "
+ id="path5717"
+ sodipodi:nodetypes="cccssccssccccsscccccccccsscccsccccscccssccccccccssccccccsscccssc" />
+ <path
+ style="color:black;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -19.970667,-15.725763 C -29.301506,-16.86873 -38.90351,-14.334411 -41.403702,-10.0688 C -43.903894,-5.80319 -38.360176,-1.413616 -29.029333,-0.270654 C -19.698494,0.872312 -10.096491,-1.662006 -7.596298,-5.927617 C -5.096106,-10.193227 -10.639827,-14.582797 -19.970667,-15.725763 z M -23.745287,-14.087957 C -22.764752,-14.012649 -22.633104,-13.893164 -22.665122,-13.778165 L -24.646705,-10.39736 C -24.751796,-10.218063 -25.091591,-10.099609 -25.585775,-10.009532 C -25.641296,-9.997359 -25.702736,-9.9942358 -25.760965,-9.9866215 C -26.287461,-9.9516732 -26.697439,-9.9756686 -26.984548,-10.106922 L -32.419156,-12.591789 C -32.640716,-12.724967 -32.435249,-12.940626 -30.145863,-13.600051 C -30.132815,-13.60381 -30.120781,-13.606364 -30.107589,-13.610152 C -28.68317,-13.907401 -27.116658,-14.08681 -25.524626,-14.143226 C -24.805549,-14.127432 -24.140912,-14.118345 -23.745287,-14.087957 z M -18.976028,-13.503755 C -18.568755,-13.45824 -17.848925,-13.311123 -16.891664,-13.085744 C -15.574628,-12.672987 -14.414213,-12.159548 -13.505739,-11.576535 C -13.497101,-11.57099 -13.492239,-11.564597 -13.483642,-11.559038 C -12.222208,-10.464662 -12.280141,-10.230931 -12.617677,-10.166238 L -20.041956,-9.2565005 C -20.476646,-9.2032501 -20.889531,-9.3057348 -21.359849,-9.4918833 C -21.689305,-9.6828511 -21.854016,-9.8631088 -21.748928,-10.042401 L -19.767345,-13.423206 C -19.688905,-13.508737 -19.515929,-13.564091 -18.976028,-13.503755 z M -34.690283,-11.642422 C -34.642473,-11.635609 -34.587814,-11.624171 -34.547445,-11.610135 L -29.104749,-9.1390678 C -28.817638,-9.0078183 -28.776142,-8.8164294 -28.852587,-8.5757414 C -28.868846,-8.5481673 -28.884947,-8.5205319 -28.901116,-8.4929462 C -29.098158,-8.2670306 -29.34342,-8.1107727 -29.73562,-8.0627297 L -37.151812,-7.166791 C -37.50958,-7.145975 -37.838846,-7.323217 -37.951486,-8.5071006 C -37.885384,-8.8691505 -37.75289,-9.2323053 -37.539999,-9.5955223 C -37.327107,-9.9587393 -37.044657,-10.303526 -36.705919,-10.632179 C -35.50933,-11.491146 -34.982455,-11.684042 -34.690283,-11.642422 z M -11.689174,-8.8249375 C -11.390625,-8.7941981 -11.145314,-8.5067145 -11.048514,-7.489316 C -11.114616,-7.127266 -11.24711,-6.764111 -11.460001,-6.400894 C -11.672893,-6.037677 -11.955343,-5.692891 -12.294081,-5.364239 C -13.686477,-4.364712 -14.165487,-4.286478 -14.452555,-4.386282 L -19.895251,-6.857349 C -20.182362,-6.988598 -20.223858,-7.179988 -20.147413,-7.420675 C -20.1321,-7.448389 -20.115066,-7.475864 -20.098884,-7.503471 C -19.901842,-7.729386 -19.65658,-7.885644 -19.26438,-7.933687 L -11.848188,-8.829626 C -11.797876,-8.8325545 -11.73803,-8.8299661 -11.689174,-8.8249375 z M -28.352173,-6.724861 C -28.147076,-6.689922 -27.93338,-6.625193 -27.71453,-6.543224 C -27.337985,-6.337581 -27.138978,-6.145262 -27.251072,-5.954016 L -29.232655,-2.573211 C -29.370267,-2.42316 -29.882085,-2.386523 -32.108336,-2.910672 C -33.425372,-3.32343 -34.585787,-3.83687 -35.494261,-4.419882 C -35.502899,-4.425427 -35.507761,-4.43182 -35.516358,-4.437379 C -36.777792,-5.531755 -36.719859,-5.765486 -36.382323,-5.830178 L -28.958044,-6.739916 C -28.753773,-6.764939 -28.557269,-6.7598 -28.352173,-6.724861 z M -22.526433,-6.011246 C -22.322237,-5.99321 -22.159007,-5.955122 -22.015452,-5.889495 L -16.580844,-3.404627 C -16.359285,-3.271451 -16.564752,-3.055791 -18.854137,-2.396366 C -18.867662,-2.393517 -18.878865,-2.389092 -18.892411,-2.386264 C -20.316831,-2.089016 -21.883342,-1.909607 -23.475374,-1.853191 C -25.97664,-1.908132 -26.379817,-2.05685 -26.334878,-2.218252 L -24.353295,-5.599057 C -24.248204,-5.778354 -23.908409,-5.896808 -23.414225,-5.986885 C -23.361006,-5.996586 -23.296126,-6.000744 -23.239035,-6.009795 C -22.975788,-6.027268 -22.730628,-6.029283 -22.526433,-6.011246 z "
+ id="path5715"
+ sodipodi:nodetypes="csssccccssccssccccsscccccccccsscccsccccscccssccccccccssccccccsscccssc" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="troiai"
+ style="display:none">
+ <rect
+ style="opacity:1;fill:#d3d7cf;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2947"
+ width="48"
+ height="48"
+ x="-60"
+ y="-1.3322676e-15" />
+ <g
+ id="g1996"
+ transform="matrix(1,0,0,1.011094,-59.50013,-0.849162)">
+ <path
+ id="path6409"
+ d="M 23.343751,6.7920654 C 19.276738,6.9662854 14.839018,8.953048 11.250001,12.542065 C 4.8695268,18.922539 3.6038088,28.020873 8.4375008,32.854566 C 13.271193,37.688258 22.369527,36.42254 28.750001,30.042065 C 35.130475,23.661591 36.396191,14.563256 31.562501,9.7295651 C 29.447761,7.614825 26.506983,6.6565614 23.343751,6.7920654 z M 26.281251,9.385815 C 27.385834,9.370233 28.341176,9.7585781 28.906251,10.542065 C 30.036398,12.109038 29.144776,14.67757 26.906251,16.292065 C 24.667727,17.906561 21.94265,17.952788 20.812501,16.385815 C 19.682351,14.818841 20.573976,12.25031 22.812501,10.635815 C 23.931764,9.8285671 25.176668,9.401397 26.281251,9.385815 z M 15.406251,11.823315 C 16.065529,11.818813 16.669136,12.0208 17.093751,12.448315 C 18.226056,13.588354 17.760962,15.894505 16.062501,17.604565 C 14.364039,19.314625 12.069806,19.775854 10.937501,18.635815 C 9.8051958,17.495776 10.23904,15.189625 11.937501,13.479565 C 12.999039,12.410777 14.307454,11.830818 15.406251,11.823315 z M 24.312501,20.292065 C 25.592364,20.164078 26.840524,20.507588 27.718751,21.385815 C 29.475205,23.142268 29.146778,26.332788 27.000001,28.479565 C 24.853222,30.626343 21.693954,30.923518 19.937501,29.167065 C 18.181049,27.410612 18.478224,24.251342 20.625001,22.104565 C 21.69839,21.031176 23.032638,20.420052 24.312501,20.292065 z M 13.531251,21.792065 C 14.025806,21.816698 14.502003,21.963359 14.906251,22.229565 C 16.523244,23.294391 16.540338,25.982012 14.968751,28.229565 C 13.397165,30.477119 10.835744,31.450641 9.2187508,30.385815 C 7.6017568,29.32099 7.5534138,26.633368 9.1250008,24.385815 C 10.303691,22.70015 12.047586,21.718166 13.531251,21.792065 z "
+ style="opacity:1;fill:url(#linearGradient2001);fill-opacity:1;stroke:none;stroke-width:0.9863953;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ transform="matrix(0.6512,0.648774,-0.68514,0.682587,21.74573,-2.442557)"
+ d="M 29.7995 18.629814 A 12.399623 15.884648 0 1 1 5.0002546,18.629814 A 12.399623 15.884648 0 1 1 29.7995 18.629814 z"
+ sodipodi:ry="15.884648"
+ sodipodi:rx="12.399623"
+ sodipodi:cy="18.629814"
+ sodipodi:cx="17.399878"
+ id="path7695"
+ style="opacity:0.45108696;fill:none;fill-opacity:1;stroke:white;stroke-width:1.05475736;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.702727,0.69577,-0.724088,0.716919,21.70497,-3.797766)"
+ d="M 29.7995 18.629814 A 12.399623 15.884648 0 1 1 5.0002546,18.629814 A 12.399623 15.884648 0 1 1 29.7995 18.629814 z"
+ sodipodi:ry="15.884648"
+ sodipodi:rx="12.399623"
+ sodipodi:cy="18.629814"
+ sodipodi:cx="17.399878"
+ id="path6407"
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#898b86;stroke-width:0.99074221;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ inkscape:r_cy="true"
+ inkscape:r_cx="true"
+ id="path4573"
+ d="M -38.579058,5.017711 C -39.575192,5.064032 -40.580718,5.225306 -41.617649,5.503883 C -47.935436,7.217172 -53.715475,13.011644 -55.321693,19.329399 C -56.562537,24.219761 -55.043694,27.333998 -52.681382,30.13706 L -38.427129,30.490613 C -34.532892,28.552395 -31.870353,25.873704 -30.027515,21.939981 L -30.027515,8.395851 C -32.178959,6.020531 -34.729135,4.838685 -38.579058,5.017711 z M -35.437213,7.543185 C -33.465593,7.415932 -32.00023,8.569575 -32.094763,10.460217 C -32.201842,12.708909 -34.447333,14.954386 -37.01728,15.382708 C -39.480147,15.811031 -41.075991,14.317893 -40.754746,12.283362 C -40.540584,10.14175 -38.506335,8.200131 -36.257632,7.664728 C -35.981564,7.601986 -35.698932,7.560077 -35.437213,7.543185 z M -46.075736,10.006159 C -45.362166,9.944214 -44.732045,10.137628 -44.282968,10.492331 C -43.699059,10.970164 -43.388065,11.777421 -43.553706,12.771262 C -43.874952,14.805795 -45.92512,16.825568 -47.959662,17.146811 C -48.878218,17.299902 -49.631695,17.06712 -50.117062,16.569481 C -50.632857,16.021808 -50.854477,15.16285 -50.633622,14.169007 C -50.205296,12.348638 -48.489372,10.525639 -46.561911,10.097317 C -46.396269,10.060507 -46.232722,10.019788 -46.075736,10.006159 z M -37.424394,18.113969 C -34.962414,17.924937 -32.945088,19.622687 -33.048823,22.216045 L -33.170367,22.337588 C -33.277449,25.121683 -35.85572,27.669557 -38.63983,27.776638 C -41.316859,27.883716 -43.109481,25.865404 -42.681156,23.188389 C -42.359913,20.725537 -40.130348,18.465596 -37.667481,18.144355 C -37.58717,18.134316 -37.503812,18.120067 -37.424394,18.113969 z M -48.018706,19.852871 C -46.046468,19.725568 -44.795474,21.405744 -45.101659,23.65109 L -45.223203,23.742247 C -45.651525,26.312182 -47.882552,28.466501 -50.024176,28.573581 C -52.1658,28.680661 -53.355085,26.766507 -52.819679,24.410733 C -52.284273,22.162039 -50.357107,20.127802 -48.322565,19.913642 C -48.222177,19.898584 -48.115702,19.859132 -48.018706,19.852871 z "
+ style="fill:none;fill-rule:nonzero;stroke:#333;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="ccccccccccccsccsccscsccccccsccccscsc" />
+ <path
+ style="fill:url(#radialGradient4556);fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -31.134002,18.15152 C -31.134002,23.988211 -36.75044,29.60465 -42.697258,29.60465 C -47.652939,29.60465 -50.075717,25.419852 -48.864328,20.79455 C -47.763065,16.719879 -44.128899,12.975586 -40.054228,11.98445 C -35.428926,10.773061 -31.134002,13.195838 -31.134002,18.15152 z "
+ id="path6440"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ <path
+ style="fill:url(#radialGradient4553);fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -32.785896,16.499626 C -32.785896,22.336317 -38.402334,27.952756 -44.349152,27.952756 C -49.304833,27.952756 -51.727611,23.767958 -50.516222,19.142656 C -49.414959,15.067985 -45.780793,11.323692 -41.706121,10.332556 C -37.08082,9.121167 -32.785896,11.543945 -32.785896,16.499626 z "
+ id="path6447"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ <path
+ style="fill:#3b3b3b;fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -29.933771,7.80123 L -29.933771,7.89498 C -29.881074,7.947677 -29.797562,7.996739 -29.746271,8.05123 C -29.806239,7.964969 -29.870965,7.886406 -29.933771,7.80123 z M -29.621271,8.23873 C -29.599292,8.271684 -29.549097,8.299377 -29.527521,8.33248 C -29.555555,8.299773 -29.592772,8.270872 -29.621271,8.23873 z M -29.496271,8.39498 C -29.314768,8.677052 -29.18039,8.946614 -29.027521,9.23873 L -29.027521,9.01998 C -29.17723,8.802667 -29.329473,8.592905 -29.496271,8.39498 z M -29.027521,20.98873 C -30.905932,25.474179 -34.641465,29.540397 -39.183771,31.61373 L -37.715021,31.61373 C -35.635627,30.548069 -33.639287,29.044383 -31.902521,27.08248 C -30.71006,25.757523 -29.751545,24.332263 -29.027521,22.89498 L -29.027521,20.98873 z "
+ id="path6490" />
+ <path
+ style="fill:url(#linearGradient2925);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2917);stroke-width:0.99999952px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
+ d="M -39.696441,31.175733 C -39.696441,31.175733 -33.791399,25.669679 -25.063258,29.047517 C -16.396087,32.401758 -14.428591,44.571406 -14.428591,44.571406 L -30.455755,44.571406 C -30.455755,44.571406 -28.618268,36.71605 -29.849221,32.492131 C -31.080173,28.268212 -39.696441,31.175733 -39.696441,31.175733 z "
+ id="path6728"
+ sodipodi:nodetypes="czcczc" />
+ <path
+ style="opacity:0.2;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:0.99999976px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -35.812505,29.341692 C -35.812505,29.341692 -32.693178,26.844865 -25.326869,29.862872 C -18.012016,32.859795 -15.687497,43.50002 -15.687497,43.50002 L -29.02648,43.50002 C -29.02648,43.50002 -27.928705,35.945603 -28.967596,32.171645 C -30.006485,28.397688 -35.812505,29.341692 -35.812505,29.341692 z "
+ id="path7675"
+ sodipodi:nodetypes="czcczc" />
+ <path
+ style="opacity:1;fill:url(#linearGradient6380);fill-opacity:1;stroke:none;stroke-width:0.9863953;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -38.040516,4.4309723 C -42.107529,4.6051923 -46.545249,6.5919553 -50.134266,10.180972 C -56.51474,16.561446 -57.780458,25.65978 -52.946766,30.493473 C -48.113074,35.327165 -39.01474,34.061447 -32.634266,27.680972 C -26.253792,21.300498 -24.988076,12.202163 -29.821766,7.3684723 C -31.936506,5.2537323 -34.877284,4.2954683 -38.040516,4.4309723 z M -35.103016,7.0247223 C -33.998433,7.0091403 -33.043091,7.3974853 -32.478016,8.1809723 C -31.347869,9.7479453 -32.239491,12.316477 -34.478016,13.930972 C -36.71654,15.545468 -39.441617,15.591695 -40.571766,14.024722 C -41.701916,12.457748 -40.810291,9.8892173 -38.571766,8.2747223 C -37.452503,7.4674743 -36.207599,7.0403043 -35.103016,7.0247223 z M -45.978016,9.4622223 C -45.318738,9.4577203 -44.715131,9.6597073 -44.290516,10.087222 C -43.158211,11.227261 -43.623305,13.533412 -45.321766,15.243472 C -47.020228,16.953532 -49.314461,17.414761 -50.446766,16.274722 C -51.579071,15.134683 -51.145227,12.828532 -49.446766,11.118472 C -48.385228,10.049684 -47.076813,9.4697253 -45.978016,9.4622223 z M -37.071766,17.930972 C -35.791903,17.802985 -34.543743,18.146495 -33.665516,19.024722 C -31.909062,20.781175 -32.237489,23.971695 -34.384266,26.118472 C -36.531045,28.26525 -39.690313,28.562425 -41.446766,26.805972 C -43.203218,25.049519 -42.906043,21.890249 -40.759266,19.743472 C -39.685877,18.670083 -38.351629,18.058959 -37.071766,17.930972 z M -47.853016,19.430972 C -47.358461,19.455605 -46.882264,19.602266 -46.478016,19.868472 C -44.861023,20.933298 -44.843929,23.620919 -46.415516,25.868472 C -47.987102,28.116026 -50.548523,29.089548 -52.165516,28.024722 C -53.78251,26.959897 -53.830853,24.272275 -52.259266,22.024722 C -51.080576,20.339057 -49.336681,19.357073 -47.853016,19.430972 z "
+ id="path4541" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#898b86;stroke-width:0.9863953;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4539"
+ sodipodi:cx="17.399878"
+ sodipodi:cy="18.629814"
+ sodipodi:rx="12.399623"
+ sodipodi:ry="15.884648"
+ d="M 29.7995 18.629814 A 12.399623 15.884648 0 1 1 5.0002546,18.629814 A 12.399623 15.884648 0 1 1 29.7995 18.629814 z"
+ transform="matrix(0.706206,0.706206,-0.727673,0.727673,-40.48126,-6.884929)" />
+ <path
+ style="fill:#e6e6e6;fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -50.357005,16.385341 C -49.576197,17.166149 -47.568402,17.500781 -45.337521,15.492988 C -43.887448,14.15446 -42.54892,11.365859 -44.445169,9.9157851 C -42.102745,12.25821 -47.23377,18.281589 -50.468549,16.385341 L -50.357005,16.385341 z "
+ id="path6480"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ <path
+ style="fill:#e6e6e6;fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -39.556216,14.843811 C -38.788515,15.611511 -35.827386,15.721184 -33.633957,13.747096 C -31.659871,11.882682 -31.0758,9.5438664 -32.720871,8.0084664 C -29.869413,11.737296 -36.046729,16.488883 -39.446544,14.953483 L -39.556216,14.843811 z "
+ id="path6488"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#e6e6e6;fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -41.088696,27.153796 C -35.597743,30.851783 -29.322372,22.111085 -33.916841,18.861339 C -29.658552,23.231688 -36.158046,29.955302 -41.088696,27.153796 L -41.088696,27.153796 z "
+ id="path6476"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ <path
+ style="fill:#e6e6e6;fill-rule:nonzero;stroke:none;stroke-miterlimit:4"
+ d="M -52.387455,28.305461 C -51.547401,29.145515 -49.267256,29.38553 -46.987113,27.3454 C -44.466952,24.945248 -44.106928,21.225012 -46.147059,19.784919 C -43.266875,23.505157 -48.547212,30.105576 -52.387455,28.425469 L -52.387455,28.305461 z "
+ id="path6484"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.45108696;fill:none;fill-opacity:1;stroke:white;stroke-width:1.06059182;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6401"
+ sodipodi:cx="17.399878"
+ sodipodi:cy="18.629814"
+ sodipodi:rx="12.399623"
+ sodipodi:ry="15.884648"
+ d="M 29.7995 18.629814 A 12.399623 15.884648 0 1 1 5.0002546,18.629814 A 12.399623 15.884648 0 1 1 29.7995 18.629814 z"
+ transform="matrix(0.6512,0.648774,-0.68514,0.682587,-40.31421,-5.005059)" />
+ <path
+ style="opacity:0.3;fill:url(#linearGradient2933);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -28,44 L -27.41815,37 L -19.297991,37.050508 L -17,44 L -28,44 z "
+ id="path6738"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="opacity:0.3;fill:#ddd;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -27.642397,35.0625 L -28.182068,31.244481 L -23.056464,31.118212 L -20.784826,35.0625 L -27.642397,35.0625 z "
+ id="path7629"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="opacity:0.3;fill:#ddd;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -29.51056,30.094209 L -32.000539,29.067704 L -28.750304,29.088145 L -25.729598,30.129981 L -29.51056,30.094209 z "
+ id="path7631"
+ sodipodi:nodetypes="ccccc" />
+ <g
+ id="g2872"
+ transform="translate(-110,-40)">
+ <path
+ transform="matrix(0.992983,0,0,2.142753,48.71484,-19.0262)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2862"
+ style="opacity:1;color:black;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.275528,0,0,0.59456,65.90022,-8.860351)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2864"
+ style="opacity:1;color:black;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.275528,0,0,0.594561,56.9002,0.139637)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2866"
+ style="opacity:1;color:black;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.275528,0,0,0.594561,74.9002,0.139637)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2868"
+ style="opacity:1;color:black;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.275528,0,0,0.59456,65.90022,9.13965)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2870"
+ style="opacity:1;color:black;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ transform="matrix(0.496284,0,0,0.535015,0.6124,75.37678)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2005"
+ style="color:black;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:1.96992087;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.496284,0,0,0.535015,0.6124,69.37678)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2007"
+ style="color:black;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:1.96992087;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <g
+ id="g2041"
+ transform="translate(-47,-50)">
+ <path
+ transform="matrix(0.965804,0,0,2.084105,49.36585,-18.30016)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2043"
+ style="opacity:1;color:black;fill:yellow;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535913,66.55122,-8.134333)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2045"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535914,57.55119,0.865653)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2047"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535914,75.55119,0.865653)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2049"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535913,66.55122,9.865668)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2051"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ style="opacity:1;color:black;fill:navy;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -20.3125,-55.625 C -21.753667,-55.578358 -23.13,-55.208382 -23.90625,-54.59375 C -25.148253,-53.610338 -24.401205,-52.349026 -22.25,-51.78125 C -20.098794,-51.213474 -17.335753,-51.547838 -16.09375,-52.53125 C -14.851747,-53.514662 -15.598797,-54.775974 -17.75,-55.34375 C -18.556702,-55.556666 -19.4478,-55.652985 -20.3125,-55.625 z M -32.59375,-54.09375 C -34.034917,-54.047106 -35.411249,-53.708385 -36.1875,-53.09375 C -37.429503,-52.110333 -36.682455,-50.849026 -34.53125,-50.28125 C -32.380044,-49.713474 -29.648252,-50.047833 -28.40625,-51.03125 C -27.164247,-52.014667 -27.880046,-53.275974 -30.03125,-53.84375 C -30.837952,-54.056666 -31.72905,-54.121736 -32.59375,-54.09375 z M -17.03125,-50 C -18.472416,-49.953356 -19.817498,-49.583385 -20.59375,-48.96875 C -21.835753,-47.985333 -21.119952,-46.724026 -18.96875,-46.15625 C -16.817544,-45.588474 -14.0545,-45.922833 -12.8125,-46.90625 C -11.570497,-47.889667 -12.317544,-49.150974 -14.46875,-49.71875 C -15.275452,-49.931666 -16.16655,-50.027986 -17.03125,-50 z M -29.3125,-48.5 C -30.753667,-48.453358 -32.13,-48.083383 -32.90625,-47.46875 C -34.148253,-46.485338 -33.401205,-45.224026 -31.25,-44.65625 C -29.098794,-44.088474 -26.335753,-44.422838 -25.09375,-45.40625 C -23.851747,-46.389662 -24.598797,-47.650974 -26.75,-48.21875 C -27.556702,-48.431666 -28.4478,-48.527985 -29.3125,-48.5 z "
+ id="path3013" />
+ <g
+ id="g3055"
+ transform="translate(0,-20)">
+ <path
+ transform="matrix(0.965804,0,0,0.952734,2.36585,-1.79436)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2052"
+ style="color:black;fill:yellow;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.23396,0,0,0.200025,19.89589,2.690751)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2054"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.244989,10.55119,5.654657)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2056"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.244989,28.55119,5.654657)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2058"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.268112,0,0,0.237961,19.07786,10.41845)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2060"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ style="display:inline"
+ id="g2087"
+ transform="matrix(0.885715,0,0,0.885715,1.28563,-23.14286)">
+ <path
+ transform="matrix(0.965804,0,0,2.084105,49.36585,-18.30016)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2089"
+ style="opacity:1;color:black;fill:yellow;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535913,66.55122,-8.134333)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2091"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535914,57.55119,0.865653)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2093"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535914,75.55119,0.865653)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2095"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535913,66.55122,9.865668)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2097"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g3041"
+ transform="translate(50,0)">
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="rect2067"
+ d="M 10.15625,2 L 5.84375,18 L 45.1875,17.96875 L 40.875,1.96875 L 10.15625,2 z "
+ style="opacity:0.8;color:black;fill:black;fill-opacity:1;fill-rule:nonzero;stroke:black;stroke-width:2.034971;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <g
+ style="opacity:0.5"
+ id="g2149">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:white;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 9.9878833,19.052816 L 13.5,1"
+ id="path2141" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:white;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 20.417708,19 L 21.4375,1"
+ id="path2143" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:white;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 30.670757,19.008622 L 29.43332,1"
+ id="path2145" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:white;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 41,19 L 37.375,1"
+ id="path2147" />
+ </g>
+ </g>
+ <g
+ id="g3070"
+ transform="translate(-50,0)">
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3062"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.23396,0,0,0.200025,19.89589,2.69075)" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3064"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.24835,0,0,0.244989,10.55119,5.65466)" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3066"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.24835,0,0,0.244989,28.55119,5.65466)" />
+ <path
+ sodipodi:type="arc"
+ style="color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3068"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.268112,0,0,0.237961,19.07786,10.10595)" />
+ </g>
+ <g
+ style="display:inline"
+ id="g2121"
+ transform="matrix(1.143947,0,0,1.143947,-57.43614,69.9204)">
+ <path
+ transform="matrix(0.965804,0,0,2.084105,49.36585,-18.30016)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2123"
+ style="opacity:1;color:black;fill:yellow;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535913,66.55122,-8.134333)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2125"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535914,57.55119,0.865653)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2127"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535914,75.55119,0.865653)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2129"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50792766;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.24835,0,0,0.535913,66.55122,9.865668)"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ sodipodi:ry="8.3968925"
+ sodipodi:rx="18.119612"
+ sodipodi:cy="12.379497"
+ sodipodi:cx="23.953241"
+ id="path2131"
+ style="opacity:1;color:black;fill:#333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.50793171;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ style="opacity:1;color:black;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 25.5,-110 C 15.840004,-110 8,-102.16 8,-92.5 C 8,-82.839997 15.840003,-75.000001 25.5,-75 C 35.159996,-75 42.999999,-82.839996 43,-92.5 C 43,-102.16 35.159996,-110 25.5,-110 z M 22.78125,-106.5625 C 23.771011,-106.65716 23.96582,-106.43877 24,-106.1875 L 24,-97.53125 C 24,-97.098147 23.698891,-96.743295 23.21875,-96.40625 C 22.703153,-96.173154 22.272538,-96.101023 21.90625,-96.3125 L 14.40625,-100.65625 C 14.101129,-100.89247 14.127611,-101.41158 16.28125,-103.6875 C 17.547068,-104.73229 18.990254,-105.55506 20.5625,-106.125 C 21.517426,-106.34785 22.293718,-106.51587 22.78125,-106.5625 z M 27.71875,-106.5625 C 28.199799,-106.5731 29.094846,-106.43834 30.4375,-106.125 C 32.009746,-105.55506 33.452932,-104.73229 34.71875,-103.6875 C 36.872391,-101.41158 36.898871,-100.89247 36.59375,-100.65625 L 29.09375,-96.3125 C 28.704021,-96.087487 28.247182,-96.197159 27.6875,-96.46875 C 27.261153,-96.786983 27,-97.125216 27,-97.53125 L 27,-106.1875 C 27.027344,-106.38852 27.163086,-106.55025 27.71875,-106.5625 z M 12.75,-98.0625 C 12.800892,-98.060484 12.85931,-98.050457 12.90625,-98.03125 L 20.40625,-93.71875 C 20.777361,-93.504491 20.915056,-93.074699 20.96875,-92.5 C 20.915056,-91.925303 20.777362,-91.495512 20.40625,-91.28125 L 12.90625,-86.96875 C 12.552459,-86.823979 12.098588,-87.108887 11.21875,-90.03125 C 11.078701,-90.83988 11,-91.651538 11,-92.5 C 11,-93.348462 11.078701,-94.160121 11.21875,-94.96875 C 11.981853,-97.50338 12.417314,-98.075679 12.75,-98.0625 z M 38.25,-98.0625 C 38.582686,-98.075681 39.018147,-97.503381 39.78125,-94.96875 C 39.921299,-94.160121 40,-93.348462 40,-92.5 C 40,-91.651537 39.921299,-90.83988 39.78125,-90.03125 C 38.901414,-87.108889 38.447541,-86.823983 38.09375,-86.96875 L 30.59375,-91.28125 C 30.242115,-91.484265 30.093674,-91.877907 30.03125,-92.40625 C 30.033454,-92.437295 30.028432,-92.469841 30.03125,-92.5 C 30.028432,-92.530159 30.033454,-92.562704 30.03125,-92.59375 C 30.093675,-93.122092 30.242114,-93.515732 30.59375,-93.71875 L 38.09375,-98.03125 C 38.14069,-98.050458 38.199108,-98.060484 38.25,-98.0625 z M 22.5,-88.8125 C 22.717889,-88.791765 22.960951,-88.710299 23.21875,-88.59375 C 23.698891,-88.256705 24,-87.901854 24,-87.46875 L 24,-78.8125 C 23.948984,-78.437453 23.456065,-78.199723 20.5625,-78.875 C 18.990254,-79.444936 17.547068,-80.267713 16.28125,-81.3125 C 14.127611,-83.588422 14.101129,-84.107533 14.40625,-84.34375 L 21.90625,-88.6875 C 22.089394,-88.793239 22.282111,-88.833235 22.5,-88.8125 z M 28.53125,-88.8125 C 28.738699,-88.827238 28.917932,-88.789009 29.09375,-88.6875 L 36.59375,-84.34375 C 36.898872,-84.107536 36.87239,-83.588424 34.71875,-81.3125 C 33.452932,-80.267713 32.009746,-79.444936 30.4375,-78.875 C 27.543935,-78.199723 27.051016,-78.437453 27,-78.8125 L 27,-87.46875 C 27,-87.918512 27.329248,-88.27628 27.84375,-88.625 C 28.088135,-88.730054 28.323801,-88.797762 28.53125,-88.8125 z "
+ id="path2064" />
+ <g
+ id="g2979"
+ transform="translate(0,-20)">
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:black;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path2971"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.965804,0,0,2.084105,2.36585,-148.3002)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:black;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path2973"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.800238,0,0,1.726831,6.331715,-143.8773)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:black;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path2975"
+ sodipodi:cx="23.953241"
+ sodipodi:cy="12.379497"
+ sodipodi:rx="18.119612"
+ sodipodi:ry="8.3968925"
+ d="M 42.072853 12.379497 A 18.119612 8.3968925 0 1 1 5.8336296,12.379497 A 18.119612 8.3968925 0 1 1 42.072853 12.379497 z"
+ transform="matrix(0.248349,0,0,0.535911,19.55124,-129.1343)" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccc"
+ id="path2977"
+ d="M 24,-137.15625 L 20,-136 C 23.5,-136.875 23.945312,-136.58954 24,-136.1875 L 24,-127.53125 C 24,-126.98987 23.563049,-126.57154 22.84375,-126.15625 L 22.84375,-118.84375 C 23.563049,-118.42846 24,-118.01013 24,-117.46875 L 24,-108.8125 C 23.945312,-108.41046 23.5,-108.125 20,-109 L 24,-107.84375 L 27,-107.84375 L 31,-109 C 27.5,-108.125 27.054688,-108.41046 27,-108.8125 L 27,-117.46875 C 27,-118.01013 27.436951,-118.42846 28.15625,-118.84375 L 28.15625,-126.15625 C 27.436951,-126.57154 27,-126.98987 27,-127.53125 L 27,-136.1875 C 27.054688,-136.58954 27.5,-136.875 31,-136 L 27,-137.15625 L 24,-137.15625 z "
+ style="opacity:0.49536671;color:black;fill:navy;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ </g>
+ <path
+ style="color:black;fill:#8ae234;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69588834;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 25.5,-200 C 15.840004,-200 8,-192.16 8,-182.5 C 8,-172.84 15.840003,-164.99999 25.5,-165 C 35.159996,-165 42.999999,-172.84 43,-182.5 C 43,-192.16 35.159996,-200 25.5,-200 z M 22.78125,-195.5625 C 23.771011,-195.65716 23.96582,-195.43877 24,-195.1875 L 24,-187.53125 C 24,-187.12521 23.738847,-186.78698 23.3125,-186.46875 C 23.265762,-186.42866 23.208184,-186.40616 23.15625,-186.375 C 22.66748,-186.16489 22.257887,-186.10948 21.90625,-186.3125 L 15.25,-190.15625 C 14.960591,-190.3803 15.03696,-190.88915 16.875,-192.875 C 16.885475,-192.88632 16.895653,-192.89483 16.90625,-192.90625 C 18.113844,-193.90298 19.525406,-194.6875 21.03125,-195.21875 C 21.734767,-195.37149 22.381902,-195.52431 22.78125,-195.5625 z M 27.71875,-195.5625 C 28.137914,-195.57174 28.916508,-195.4472 29.96875,-195.21875 C 31.474594,-194.6875 32.886156,-193.90298 34.09375,-192.90625 C 34.105233,-192.89677 34.113549,-192.88452 34.125,-192.875 C 35.96304,-190.88915 36.039409,-190.3803 35.75,-190.15625 L 29.09375,-186.3125 C 28.70402,-186.08748 28.247182,-186.19716 27.6875,-186.46875 C 27.261153,-186.78698 27,-187.12522 27,-187.53125 L 27,-195.1875 C 27.027344,-195.38852 27.163086,-195.55025 27.71875,-195.5625 z M 13.59375,-187.5625 C 13.643788,-187.56048 13.70306,-187.55046 13.75,-187.53125 L 20.40625,-183.71875 C 20.757886,-183.51574 20.906324,-183.12209 20.96875,-182.59375 C 20.968656,-182.53128 20.96875,-182.46872 20.96875,-182.40625 C 20.906325,-181.87791 20.757887,-181.48427 20.40625,-181.28125 L 13.75,-177.46875 C 13.416208,-177.33217 12.997814,-177.62145 12.21875,-180.09375 C 12.077623,-180.87584 12,-181.67745 12,-182.5 C 12,-183.32255 12.077623,-184.12416 12.21875,-184.90625 C 12.888257,-187.03088 13.28797,-187.57482 13.59375,-187.5625 z M 37.40625,-187.5625 C 37.71203,-187.57482 38.111743,-187.03088 38.78125,-184.90625 C 38.922377,-184.12416 39,-183.32255 39,-182.5 C 39,-181.67745 38.922377,-180.87584 38.78125,-180.09375 C 38.002186,-177.62145 37.583791,-177.33217 37.25,-177.46875 L 30.59375,-181.28125 C 30.242114,-181.48426 30.093676,-181.87791 30.03125,-182.40625 C 30.030351,-182.46877 30.03125,-182.53123 30.03125,-182.59375 C 30.093675,-183.12209 30.242113,-183.51573 30.59375,-183.71875 L 37.25,-187.53125 C 37.29694,-187.55046 37.356212,-187.56048 37.40625,-187.5625 z M 22.5,-178.8125 C 22.717889,-178.79176 22.960951,-178.7103 23.21875,-178.59375 C 23.698891,-178.2567 24,-177.90185 24,-177.46875 L 24,-169.8125 C 23.952029,-169.45984 23.478393,-169.24996 21.03125,-169.78125 C 19.525406,-170.3125 18.113844,-171.09702 16.90625,-172.09375 C 16.894767,-172.10323 16.886451,-172.11548 16.875,-172.125 C 15.03696,-174.11085 14.960591,-174.6197 15.25,-174.84375 L 21.90625,-178.6875 C 22.089394,-178.79324 22.282111,-178.83324 22.5,-178.8125 z M 28.53125,-178.8125 C 28.738699,-178.82724 28.917931,-178.78901 29.09375,-178.6875 L 35.75,-174.84375 C 36.039408,-174.6197 35.963039,-174.11085 34.125,-172.125 C 34.113549,-172.11548 34.105233,-172.10323 34.09375,-172.09375 C 32.886155,-171.09702 31.474594,-170.3125 29.96875,-169.78125 C 27.521607,-169.24996 27.047971,-169.45984 27,-169.8125 L 27,-177.46875 C 27,-177.87479 27.261153,-178.21302 27.6875,-178.53125 C 27.733414,-178.56552 27.793729,-178.5911 27.84375,-178.625 C 28.088135,-178.73005 28.323801,-178.79776 28.53125,-178.8125 z "
+ id="path5693"
+ sodipodi:nodetypes="csssccccssccssccccsscccccccccsscccsccccscccssccccccccssccccccsscccssc" />
+ </g>
+</svg>
diff --git a/rapid/media.py b/rapid/media.py
index def73e6..4ed5380 100755
--- a/rapid/media.py
+++ b/rapid/media.py
@@ -21,17 +21,27 @@ import os
import config
import common
+import metadata
+import videometadata
import operator
-def getDefaultPhotoLocation():
- for default in config.DEFAULT_PHOTO_LOCATIONS:
+def _getDefaultLocation(options, ignore_missing_dir=False):
+ if ignore_missing_dir:
+ return common.getFullPath(options[0])
+ for default in options:
path = common.getFullPath(default)
if os.path.isdir(path):
return path
return common.getFullPath('')
+
+def getDefaultPhotoLocation(ignore_missing_dir=False):
+ return _getDefaultLocation(config.DEFAULT_PHOTO_LOCATIONS, ignore_missing_dir)
+
+def getDefaultVideoLocation(ignore_missing_dir=False):
+ return _getDefaultLocation(config.DEFAULT_VIDEO_LOCATIONS, ignore_missing_dir)
-def isImageMedia(path):
+def is_DCIM_Media(path):
""" Returns true if directory specifies some media with photos on it """
if os.path.isdir(os.path.join(path, "DCIM")):
@@ -41,27 +51,60 @@ def isImageMedia(path):
return False
-def isBackupMedia(path, identifier, writeable=True):
- """ Test to see if path is used as a backup medium for storing images
+def isBackupMedia(path, identifiers, writeable=True):
+ """ Test to see if path is used as a backup medium for storing photos or videos
+
+ Identifiers is expected to be a list of folder names to check to see
+ if the path is a backup path. Only one of them needs to be present
+ for the path to be considered a backup medium.
If writeable is True, the directory must be writeable by the user """
suitable = False
- if os.path.isdir(os.path.join(path, identifier)):
- if writeable:
- suitable = os.access(os.path.join(path, identifier), os.W_OK)
- else:
- suitable = True
- return suitable
+
+ for identifier in identifiers:
+ if os.path.isdir(os.path.join(path, identifier)):
+ if writeable:
+ suitable = os.access(os.path.join(path, identifier), os.W_OK)
+ else:
+ suitable = True
+ if suitable:
+ return True
+ return False
def isImage(fileName):
ext = os.path.splitext(fileName)[1].lower()[1:]
- return (ext in config.RAW_FILE_EXTENSIONS) or (ext in config.NON_RAW_IMAGE_FILE_EXTENSIONS)
+ return (ext in metadata.RAW_FILE_EXTENSIONS) or (ext in metadata.NON_RAW_IMAGE_FILE_EXTENSIONS)
+
+def isVideo(fileName):
+ ext = os.path.splitext(fileName)[1].lower()[1:]
+ return (ext in videometadata.VIDEO_FILE_EXTENSIONS)
+
+def getVideoThumbnailFile(fullFileName):
+ """
+ Checks to see if a thumbnail file is in the same directory as the
+ file. Expects a full path to be part of the file name.
+
+ Returns the filename, including path, if found, else returns None.
+ """
+
+ f = None
+ name, ext = os.path.splitext(fullFileName)
+ for e in videometadata.VIDEO_THUMBNAIL_FILE_EXTENSIONS:
+ if os.path.exists(name + '.' + e):
+ f = name + '.' + e
+ break
+ if os.path.exists(name + '.' + e.upper()):
+ f = name + '.' + e.upper()
+ break
+
+ return f
+
class Media:
- """ Generic class for media holding images """
+ """ Generic class for media holding images and videos """
def __init__(self, path, volume = None):
"""
- volume is a gnomevfs or gio volume, see class Volume in rapid.py
+ volume is a gnomevfs or gio volume: see class Volume in rapid.py
"""
self.path = path
@@ -94,87 +137,39 @@ class Media:
class CardMedia(Media):
- """Compact Flash cards, etc."""
+ """Compact Flash cards, hard drives, etc."""
def __init__(self, path, volume = None, doNotScan=True):
"""
volume is a gnomevfs or gio volume, see class Volume in rapid.py
"""
Media.__init__(self, path, volume)
- if not doNotScan:
- self.scanMedia()
- def scanMedia(self):
- """ creates a list of images on a path, recursively scanning
-
- images are sorted by modification time"""
-
- self.images = []
- self.imageSizeSum = 0
- for root, dirs, files in os.walk(self.path):
- for name in files:
- if isImage(name):
- image = os.path.join(root, name)
- size = os.path.getsize(image)
- modificationTime = os.path.getmtime(image)
- self.images.append((name, root, size, modificationTime),)
- self.imageSizeSum += size
- self.images.sort(key=operator.itemgetter(3))
- self.noImages = len(self.images)
- def setMedia(self, images, imageSizeSum, noImages):
- self.images = images
- self.imageSizeSum = imageSizeSum
- self.noImages = noImages
+ def setMedia(self, imagesAndVideos, fileSizeSum, noFiles):
+ self.imagesAndVideos = imagesAndVideos
+ self.fileSizeSum = fileSizeSum
+ self.noFiles = noFiles
- def numberOfImages(self):
- return self.noImages
+ def numberOfImagesAndVideos(self):
+ return self.noFiles
- def sizeOfImages(self, humanReadable = True):
+ def sizeOfImagesAndVideos(self, humanReadable = True):
if humanReadable:
- return common.formatSizeForUser(self.imageSizeSum)
+ return common.formatSizeForUser(self.fileSizeSum)
else:
- return self.imageSizeSum
+ return self.fileSizeSum
- def firstImage(self):
- if self.images:
- return self.images[0]
+ def _firstFile(self, isCorrectFile):
+ if self.imagesAndVideos:
+ for i in range(len(self.imagesAndVideos)):
+ if isCorrectFile(self.imagesAndVideos[i]):
+ return self.imagesAndVideos[i]
else:
return None
-
-def scanForImageMedia(path):
- """ returns a list of paths that contain images on media produced by a digital camera """
-
- media = []
- for i in os.listdir(path):
- p = os.path.join(path, i)
- if os.path.isdir(p):
- if isImageMedia(p):
- media.append(p)
- return media
-
-def scanForBackupMedia(path, identifier):
- """ returns a list of paths that contains backed up images """
-
- media = []
- for i in os.listdir(path):
- p = os.path.join(path, i)
- if os.path.isdir(p):
- if isBackupMedia(p, identifier):
- media.append(os.path.join(p, identifier))
- return media
-
+ def firstImage(self):
+ return self._firstFile(isImage)
-if __name__ == '__main__':
- print "Card media:"
- for m in scanForImageMedia('/media'):
- media = CardMedia(m)
- print media.prettyName()
- print media.numberOfImages()
- print media.sizeOfImages()
+ def firstVideo(self):
+ return self._firstFile(isVideo)
- print "\nBackup media:"
- for m in scanForBackupMedia('/media', 'photos'):
- print m
-
- print "\nDefault download folder: ", getDefaultPhotoLocation()
diff --git a/rapid/metadata.py b/rapid/metadata.py
index 1d4b2df..f8886d7 100755
--- a/rapid/metadata.py
+++ b/rapid/metadata.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: latin1 -*-
-### Copyright (C) 2007 Damon Lynch <damonlynch@gmail.com>
+### Copyright (C) 2007-10 Damon Lynch <damonlynch@gmail.com>
### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
@@ -20,7 +20,9 @@
import re
import datetime
import sys
+import subprocess
import config
+import types
try:
import pyexiv2
@@ -28,18 +30,98 @@ except ImportError:
sys.stderr.write("You need to install pyexiv2, the python binding for exiv2, to run this program.\n" )
sys.exit(1)
-#only pyexiv2 0.1.2 and 0.1.3 use the "Rational" class
-#is there a superior way to find which version of pyexiv2 is being used?
+#only pyexiv2 <= 0.1.1 does not use the "Rational" class
if 'Rational' in dir(pyexiv2):
usesRational = True
else:
usesRational = False
+#get versions of pyexiv2 and exiv2 libraries
+if 'version_info' in dir(pyexiv2):
+ pyexiv2_version = pyexiv2.version_info
+ exiv2_version = pyexiv2.exiv2_version_info
+ baseclass = eval('pyexiv2.metadata.ImageMetadata')
+else:
+ pyexiv2_version = (0,1,'x')
+ # try to determine the version of exiv2 from it's standard output
+ try:
+ proc = subprocess.Popen(['exiv2', '-V'], stdout=subprocess.PIPE)
+ output = proc.communicate()[0]
+ except:
+ output = None
+ exiv2_version = None
+ if output:
+ # assume output contains the line 'exiv2 0.x' or possibly
+ # 'exiv2 0.x.x'
+ start = output.find('exiv2 ')
+ if start < 0:
+ exiv2_version = None
+ else:
+ end = output.find('\n', start)
+ if end:
+ exiv2_v = output[6:end]
+ else:
+ exiv2_v = output[6:]
+
+ exiv2_version = []
+ dot = exiv2_v.find('.')
+ while dot > 0:
+ exiv2_version += [int(exiv2_v[:dot])]
+ exiv2_v = exiv2_v[dot+1:]
+ dot = exiv2_v.find('.')
+ exiv2_version += [int(exiv2_v)]
+ exiv2_version = tuple(exiv2_version)
+
+
+ baseclass = eval('pyexiv2.Image')
+
+def __version_info(version):
+ if not version:
+ return ''
+ else:
+ v = ''
+ for i in version:
+ v += '.%s' % i
+ return v[1:]
+
+def version_info():
+ return __version_info(pyexiv2_version)
+
+def exiv2_version_info():
+ return __version_info(exiv2_version)
+
+RAW_FILE_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mos', 'mrw',
+ 'nef', 'orf', 'pef', 'raf', 'raw', 'sr2']
-class MetaData(pyexiv2.Image):
+#exiv2 0.18.1 introduces support for Panasonic .RW2 files
+#pyexiv2 in combination with exiv2 0.18 segfaults when trying to read an
+#RW2 files, so we should not read those! exiv2 0.17 & pyexiv2 segfaults
+#with MEF files.
+
+if exiv2_version[0] > 0:
+ RAW_FILE_EXTENSIONS += ['rw2', 'mef']
+else:
+ if exiv2_version[1] > 17:
+ RAW_FILE_EXTENSIONS += ['mef']
+ if exiv2_version[1] > 18:
+ RAW_FILE_EXTENSIONS += ['rw2']
+ else:
+ if len(exiv2_version) > 2:
+ if exiv2_version[2] >= 1:
+ RAW_FILE_EXTENSIONS += ['rw2']
+
+RAW_FILE_EXTENSIONS.sort()
+
+NON_RAW_IMAGE_FILE_EXTENSIONS = ['jpg', 'jpe', 'jpeg', 'tif', 'tiff']
+
+
+class MetaData(baseclass):
"""
Class providing human readable access to image metadata
+
"""
+
+ __version01__ = pyexiv2_version[0] == 0 and pyexiv2_version[1] == 1
def aperture(self, missing=''):
"""
@@ -97,10 +179,16 @@ class MetaData(pyexiv2.Image):
try:
if usesRational:
+
e = str(self["Exif.Photo.ExposureTime"])
+
e0, e1 = e.split('/')
e0 = int(e0)
e1 = int(e1)
+ # some values, e.g. Nikon, are in the format "10/1600"
+ if (e0 > 1) and (e0 < e1):
+ e1 = e1 / e0
+ e0 = 1
else:
e0, e1 = self["Exif.Photo.ExposureTime"]
@@ -111,7 +199,7 @@ class MetaData(pyexiv2.Image):
else:
return str(e0)
else:
- return e
+ return "%s/%s" % (e0,e1)
elif e0 > e1:
e = float(e0) / e1
if alternativeFormat:
@@ -171,7 +259,7 @@ class MetaData(pyexiv2.Image):
def cameraSerial(self, missing=''):
try:
- keys = self.exifKeys()
+ keys = self.rpd_keys()
if 'Exif.Canon.SerialNumber' in keys:
v = self['Exif.Canon.SerialNumber']
elif 'Exif.Nikon3.SerialNumber' in keys:
@@ -196,9 +284,11 @@ class MetaData(pyexiv2.Image):
def shutterCount(self, missing=''):
try:
- keys = self.exifKeys()
+ keys = self.rpd_keys()
if 'Exif.Nikon3.ShutterCount' in keys:
v = self['Exif.Nikon3.ShutterCount']
+ elif 'Exif.Canon.FileNumber' in keys:
+ v = self['Exif.Canon.FileNumber']
elif 'Exif.Canon.ImageNumber' in keys:
v = self['Exif.Canon.ImageNumber']
else:
@@ -290,12 +380,13 @@ class MetaData(pyexiv2.Image):
Returns missing either metadata value is not present.
"""
- keys = self.exifKeys()
+ keys = self.rpd_keys()
try:
if "Exif.Photo.DateTimeOriginal" in keys:
- return self["Exif.Photo.DateTimeOriginal"]
+ v = self["Exif.Photo.DateTimeOriginal"]
else:
- return self["Exif.Image.DateTime"]
+ v = self["Exif.Image.DateTime"]
+ return v
except:
return missing
@@ -309,11 +400,66 @@ class MetaData(pyexiv2.Image):
def orientation(self, missing=''):
"""
Returns the orientation of the image, as recorded by the camera
+ Return type int
"""
try:
- return self['Exif.Image.Orientation']
+ v = self['Exif.Image.Orientation']
+ if isinstance(v, types.StringType):
+ # pyexiv2 >= 0.2 returns a string, not an int
+ v = int(v)
+ return v
except:
return missing
+
+ # following class methods are designed to cope with using both
+ # pyexiv2 0.1.x and pyexiv2 0.2.x
+
+ def getThumbnailData(self, max_size_needed=0):
+ """
+ Returns a thumbnail of the image.
+
+ If the image supports multiple thumbnails, and max_size_needed
+ is not 0, then it will search for the smallest thumbnail that
+ matches the size required
+
+ The image will be in whatever format the thumbnail itself is,
+ typically a jpeg or tiff.
+ """
+ if self.__version01__:
+ return pyexiv2.Image.getThumbnailData(self)[1]
+
+ else:
+ if not self.previews:
+ return None, None
+ else:
+ if max_size_needed:
+ for thumbnail in self.previews:
+ if thumbnail.dimensions[0] >= max_size_needed or thumbnail.dimensions[1] >= max_size_needed:
+ break
+ else:
+ thumbnail = self.previews[-1]
+
+ return thumbnail.data
+
+ def read(self):
+ if self.__version01__:
+ self.readMetadata()
+ else:
+ pyexiv2.metadata.ImageMetadata.read(self)
+
+ def rpd_keys(self):
+ if self.__version01__:
+ return pyexiv2.Image.exifKeys(self)
+ else:
+ return self.exif_keys
+
+ def __getitem__(self, key):
+ if self.__version01__:
+ return pyexiv2.Image.__getitem__(self, key)
+ else:
+ return pyexiv2.metadata.ImageMetadata.__getitem__(self, key).raw_value
+
+
class DummyMetaData(MetaData):
"""
@@ -376,16 +522,15 @@ class DummyMetaData(MetaData):
if __name__ == '__main__':
import sys
+
if (len(sys.argv) != 2):
print 'Usage: ' + sys.argv[0] + ' path/to/photo/containing/metadata'
m = DummyMetaData()
else:
m = MetaData(sys.argv[1])
- m.readMetadata()
+ m.read()
-# for i in m.exifKeys():
-# print i
print "f"+ m.aperture('missing ')
print "ISO " + m.iso('missing ')
print m.exposureTime(missing='missing ') + " sec"
diff --git a/rapid/prefs.py b/rapid/prefs.py
index 6912138..2f7d771 100644
--- a/rapid/prefs.py
+++ b/rapid/prefs.py
@@ -15,6 +15,7 @@
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
### Modified August 2007 by Damon Lynch to allow use of list value preferences
+### Modified May 2010 by Damon Lynch to allow preferences to be reset
"""Module to help implement 'instant-apply' preferences.
@@ -179,4 +180,13 @@ class Preferences(object):
"""
for k,v in self._prefs.items():
print k, v.type, v.current
+
+ def reset(self):
+ """
+ reset all preferences to defaults
+ """
+
+ for key in self._prefs:
+ self.__setattr__(key, self.get_default(key))
+
diff --git a/rapid/rapid.py b/rapid/rapid.py
index 1c04763..f5f221e 100755
--- a/rapid/rapid.py
+++ b/rapid/rapid.py
@@ -27,6 +27,7 @@ import time
import datetime
import atexit
import tempfile
+import types
import webbrowser
import operator
@@ -46,6 +47,7 @@ import pango
try:
import gio
using_gio = True
+ import gobject
except ImportError:
import gnomevfs
using_gio = False
@@ -68,12 +70,14 @@ import common
import misc
import higdefaults as hd
-from media import getDefaultPhotoLocation
+from media import getDefaultPhotoLocation, getDefaultVideoLocation
from media import CardMedia
import media
import metadata
+import videometadata
+from videometadata import DOWNLOAD_VIDEO
import renamesubfolderprefs as rn
@@ -99,6 +103,8 @@ _ = Configi18n._
#Translators: if neccessary, for guidance in how to translate this program, you may see http://damonlynch.net/translate.html
PROGRAM_NAME = _('Rapid Photo Downloader')
+MAX_THUMBNAIL_SIZE = 100
+
def today():
return datetime.date.today().strftime('%Y-%m-%d')
@@ -125,6 +131,7 @@ def updateDisplay(display_queue):
except tube.EOInformation:
for w in workers.getStartedWorkers():
w.join()
+
gtk.main_quit()
return False
@@ -144,7 +151,7 @@ class Queue(tube.Tube):
# this is ugly but I don't know a better way :(
display_queue = Queue()
-media_collection_treeview = image_hbox = log_dialog = None
+media_collection_treeview = thumbnail_hbox = log_dialog = None
job_code = None
need_job_code = False
@@ -344,12 +351,18 @@ workers = ThreadManager()
class RapidPreferences(prefs.Preferences):
defaults = {
"program_version": prefs.Value(prefs.STRING, ""),
- "download_folder": prefs.Value(prefs.STRING,
+ "download_folder": prefs.Value(prefs.STRING,
getDefaultPhotoLocation()),
+ "video_download_folder": prefs.Value(prefs.STRING,
+ getDefaultVideoLocation()),
"subfolder": prefs.ListValue(prefs.STRING_LIST, rn.DEFAULT_SUBFOLDER_PREFS),
+ "video_subfolder": prefs.ListValue(prefs.STRING_LIST, rn.DEFAULT_VIDEO_SUBFOLDER_PREFS),
"image_rename": prefs.ListValue(prefs.STRING_LIST, [rn.FILENAME,
rn.NAME_EXTENSION,
rn.ORIGINAL_CASE]),
+ "video_rename": prefs.ListValue(prefs.STRING_LIST, [rn.FILENAME,
+ rn.NAME_EXTENSION,
+ rn.ORIGINAL_CASE]),
"device_autodetection": prefs.Value(prefs.BOOL, True),
"device_location": prefs.Value(prefs.STRING, os.path.expanduser('~')),
"device_autodetection_psd": prefs.Value(prefs.BOOL, False),
@@ -359,6 +372,8 @@ class RapidPreferences(prefs.Preferences):
"backup_device_autodetection": prefs.Value(prefs.BOOL, True),
"backup_identifier": prefs.Value(prefs.STRING,
config.DEFAULT_BACKUP_LOCATION),
+ "video_backup_identifier": prefs.Value(prefs.STRING,
+ config.DEFAULT_VIDEO_BACKUP_LOCATION),
"backup_location": prefs.Value(prefs.STRING, os.path.expanduser('~')),
"strip_characters": prefs.Value(prefs.BOOL, True),
"auto_download_at_startup": prefs.Value(prefs.BOOL, False),
@@ -458,27 +473,46 @@ class RapidPreferences(prefs.Preferences):
else:
return ''
+ def reset(self):
+ """
+ resets all preferences to default values
+ """
+
+ prefs.Preferences.reset(self)
+ self.program_version = __version__
+
class ImageRenameTable(tpm.TablePlusMinus):
- def __init__(self, parentApp, adjustScrollWindow):
+ def __init__(self, parentApp, adjustScrollWindow):
tpm.TablePlusMinus.__init__(self, 1, 3)
self.parentApp = parentApp
self.adjustScrollWindow = adjustScrollWindow
+ if not hasattr(self, "errorTitle"):
+ self.errorTitle = _("Error in Photo Rename preferences")
+
+ self.table_type = self.errorTitle[len("Error in "):]
+ self.i = 0
+
if adjustScrollWindow:
+ self.scrollBar = self.adjustScrollWindow.get_vscrollbar()
+ #this next line does not work on early versions of pygtk :(
+ self.scrollBar.connect('visibility-notify-event', self.scrollbar_visibility_change)
self.connect("size-request", self.size_adjustment)
self.connect("add", self.size_adjustment)
- self.tableWidth = self.allocation.width
+ self.connect("remove", self.size_adjustment)
+
+ # get scrollbar thickness from parent app scrollbar - very hackish, but what to do??
+ self.bump = self.parentApp.parentApp.image_scrolledwindow.get_hscrollbar().allocation.height
+ self.haveVerticalScrollbar = False
+
# vbar is '1' if there is not vertical scroll bar
# if there is a vertical scroll bar, then it will have a the width of the bar
- self.vbar = self.adjustScrollWindow.get_vscrollbar().allocation.width
+ #self.vbar = self.adjustScrollWindow.get_vscrollbar().allocation.width
self.getParentAppPrefs()
self.getPrefsFactory()
- if not hasattr(self, "errorTitle"):
- self.errorTitle = _("Error in Image Rename preferences")
-
try:
self.prefsFactory.checkPrefsForValidity()
@@ -497,7 +531,7 @@ class ImageRenameTable(tpm.TablePlusMinus):
self.updateParentAppPrefs()
msg = "%s.\n" % e
- msg += _("Resetting to default values.")
+ msg += _("Resetting to default values." + "\n")
sys.stderr.write(msg)
@@ -507,10 +541,7 @@ class ImageRenameTable(tpm.TablePlusMinus):
for row in self.prefsFactory.getWidgetsBasedOnPreferences():
self.append(row)
-
-
-
-
+
def updatePreferences(self):
prefList = []
for row in self.pm_rows:
@@ -535,25 +566,32 @@ class ImageRenameTable(tpm.TablePlusMinus):
self.updateExample()
- def size_adjustment(self, arg1, arg2):
- """ Adjust scrolledwindow width in preferences dialog to reflect width of image rename table
+ def scrollbar_visibility_change(self, widget, event):
+ if event.state == gdk.VISIBILITY_UNOBSCURED:
+ self.haveVerticalScrollbar = True
+ self.adjustScrollWindow.set_size_request(self.adjustScrollWindow.allocation.width + self.bump, -1)
+
+
+ def size_adjustment(self, widget, arg2):
+ """
+ Adjust scrolledwindow width in preferences dialog to reflect width of image rename table
- The algorithm is complicated by the need to tak into account the presence of a vertical scrollbar"""
+ The algorithm is complicated by the need to take into account the presence of a vertical scrollbar,
+ which might be added as the user adds more rows
+
+ The pygtk code behaves inconsistently depending on the pygtk version
+ """
if self.adjustScrollWindow:
- if self.adjustScrollWindow.get_vscrollbar().allocation.width > 1:
- extra = self.adjustScrollWindow.get_vscrollbar().allocation.width + 10
+ self.haveVerticalScrollbar = self.scrollBar.allocation.width > 1 or self.haveVerticalScrollbar
+ if not self.haveVerticalScrollbar:
+ if self.allocation.width > self.adjustScrollWindow.allocation.width:
+ self.adjustScrollWindow.set_size_request(self.allocation.width, -1)
else:
- extra = 0
- if self.vbar <= 1:
- if self.allocation.width > self.tableWidth:
- self.adjustScrollWindow.set_size_request(self.allocation.width + extra, -1)
- self.tableWidth = self.allocation.width + extra
- elif self.allocation.width - extra > self.tableWidth:
- self.adjustScrollWindow.set_size_request(self.allocation.width + extra, -1)
- self.tableWidth = self.allocation.width + extra
- self.vbar = self.adjustScrollWindow.get_vscrollbar().allocation.width
-
+ if self.allocation.width > self.adjustScrollWindow.allocation.width - self.bump:
+ self.adjustScrollWindow.set_size_request(self.allocation.width + self.bump, -1)
+ self.bump = 0
+
def getParentAppPrefs(self):
self.prefList = self.parentApp.prefs.image_rename
@@ -634,12 +672,30 @@ class ImageRenameTable(tpm.TablePlusMinus):
"""
self.updatePreferences()
-class SubfolderTable(ImageRenameTable):
- def __init__(self, parentApp, adjustScollWindow):
- self.errorTitle = _("Error in Download Subfolder preferences")
+class VideoRenameTable(ImageRenameTable):
+ def __init__(self, parentApp, adjustScollWindow):
+ self.errorTitle = _("Error in Video Rename preferences")
ImageRenameTable.__init__(self, parentApp, adjustScollWindow)
def getParentAppPrefs(self):
+ self.prefList = self.parentApp.prefs.video_rename
+
+ def getPrefsFactory(self):
+ self.prefsFactory = rn.VideoRenamePreferences(self.prefList, self,
+ sequences = sequences)
+
+ def updateParentAppPrefs(self):
+ self.parentApp.prefs.video_rename = self.prefList
+
+ def updateExample(self):
+ self.parentApp.updateVideoRenameExample()
+
+class SubfolderTable(ImageRenameTable):
+ def __init__(self, parentApp, adjustScollWindow):
+ self.errorTitle = _("Error in Photo Download Subfolders preferences")
+ ImageRenameTable.__init__(self, parentApp, adjustScollWindow)
+
+ def getParentAppPrefs(self):
self.prefList = self.parentApp.prefs.subfolder
def getPrefsFactory(self):
@@ -649,8 +705,24 @@ class SubfolderTable(ImageRenameTable):
self.parentApp.prefs.subfolder = self.prefList
def updateExample(self):
- self.parentApp.updateDownloadFolderExample()
+ self.parentApp.updatePhotoDownloadFolderExample()
+
+class VideoSubfolderTable(ImageRenameTable):
+ def __init__(self, parentApp, adjustScollWindow):
+ self.errorTitle = _("Error in Video Download Subfolders preferences")
+ ImageRenameTable.__init__(self, parentApp, adjustScollWindow)
+
+ def getParentAppPrefs(self):
+ self.prefList = self.parentApp.prefs.video_subfolder
+
+ def getPrefsFactory(self):
+ self.prefsFactory = rn.VideoSubfolderPreferences(self.prefList, self)
+ def updateParentAppPrefs(self):
+ self.parentApp.prefs.video_subfolder = self.prefList
+
+ def updateExample(self):
+ self.parentApp.updateVideoDownloadFolderExample()
class PreferencesDialog(gnomeglade.Component):
def __init__(self, parentApp):
@@ -668,9 +740,13 @@ class PreferencesDialog(gnomeglade.Component):
self._setupTabSelector()
self._setupControlSpacing()
-
- # get example image data
+ if DOWNLOAD_VIDEO:
+ self.file_types = _("photos and videos")
+ else:
+ self.file_types = _("photos")
+
+ # get example photo and video data
try:
w = workers.firstWorkerReadyToDownload()
root, self.sampleImageName = w.firstImage()
@@ -680,16 +756,31 @@ class PreferencesDialog(gnomeglade.Component):
except:
self.sampleImage = metadata.DummyMetaData()
self.sampleImageName = 'IMG_0524.CR2'
+
+
+ try:
+ root, self.sampleVideoName = w.firstVideo()
+ video = os.path.join(root, self.sampleVideoName)
+ self.sampleVideo = videometadata.MetaData(video)
+ except:
+ self.sampleVideo = videometadata.DummyMetaData()
+ self.sampleVideoName = 'MVI_1379.MOV'
+
# setup tabs
- self._setupDownloadFolderTab()
+ self._setupPhotoDownloadFolderTab()
self._setupImageRenameTab()
+ self._setupVideoDownloadFolderTab()
+ self._setupVideoRenameTab()
self._setupRenameOptionsTab()
self._setupJobCodeTab()
self._setupDeviceTab()
self._setupBackupTab()
self._setupAutomationTab()
self._setupErrorTab()
+
+ if not DOWNLOAD_VIDEO:
+ self.disableVideoControls()
self.widget.realize()
@@ -698,7 +789,7 @@ class PreferencesDialog(gnomeglade.Component):
width_of_widest_sel_row = self.treeview.get_background_area(1, self.treeview_column)[2]
self.scrolled_window.set_size_request(width_of_widest_sel_row + 2, -1)
- #set the minimum width of the scolled window holding the image rename table
+ #set the minimum width of the scolled window holding the photo rename table
if self.rename_scrolledwindow.get_vscrollbar():
extra = self.rename_scrolledwindow.get_vscrollbar().allocation.width + 10
else:
@@ -732,7 +823,11 @@ class PreferencesDialog(gnomeglade.Component):
def on_download_folder_filechooser_button_selection_changed(self, widget):
self.prefs.download_folder = widget.get_current_folder()
- self.updateDownloadFolderExample()
+ self.updatePhotoDownloadFolderExample()
+
+ def on_video_download_folder_filechooser_button_selection_changed(self, widget):
+ self.prefs.video_download_folder = widget.get_current_folder()
+ self.updateVideoDownloadFolderExample()
def on_backup_folder_filechooser_button_selection_changed(self, widget):
self.prefs.backup_location = widget.get_current_folder()
@@ -744,15 +839,17 @@ class PreferencesDialog(gnomeglade.Component):
def _setupControlSpacing(self):
"""
set spacing of some but not all controls
-
- not currently used
"""
self._setupTableSpacing(self.download_folder_table)
+ self._setupTableSpacing(self.video_download_folder_table)
self.download_folder_table.set_row_spacing(2,
hd.VERTICAL_CONTROL_SPACE)
+ self.video_download_folder_table.set_row_spacing(2,
+ hd.VERTICAL_CONTROL_SPACE)
self._setupTableSpacing(self.rename_example_table)
- self.devices_table.set_col_spacing(0, hd.NESTED_CONTROLS_SPACE)
+ self._setupTableSpacing(self.video_rename_example_table)
+ self.devices_table.set_col_spacing(0, hd.NESTED_CONTROLS_SPACE)
self._setupTableSpacing(self.backup_table)
self.backup_table.set_col_spacing(1, hd.NESTED_CONTROLS_SPACE)
@@ -769,11 +866,16 @@ class PreferencesDialog(gnomeglade.Component):
table.set_col_spacing(1, hd.CONTROL_LABEL_SPACE)
def _setupSubfolderTable(self):
- self.subfolder_table = SubfolderTable(self, None)
+ self.subfolder_table = SubfolderTable(self, None)
self.subfolder_vbox.pack_start(self.subfolder_table)
self.subfolder_table.show_all()
- def _setupDownloadFolderTab(self):
+ def _setupVideoSubfolderTable(self):
+ self.video_subfolder_table = VideoSubfolderTable(self, None)
+ self.video_subfolder_vbox.pack_start(self.video_subfolder_table)
+ self.video_subfolder_table.show_all()
+
+ def _setupPhotoDownloadFolderTab(self):
self.download_folder_filechooser_button = gtk.FileChooserButton(
_("Select a folder to download photos to"))
self.download_folder_filechooser_button.set_current_folder(
@@ -789,16 +891,41 @@ class PreferencesDialog(gnomeglade.Component):
self.download_folder_filechooser_button.show()
self._setupSubfolderTable()
- self.updateDownloadFolderExample()
+ self.updatePhotoDownloadFolderExample()
+
+ def _setupVideoDownloadFolderTab(self):
+ self.video_download_folder_filechooser_button = gtk.FileChooserButton(
+ _("Select a folder to download videos to"))
+ self.video_download_folder_filechooser_button.set_current_folder(
+ self.prefs.video_download_folder)
+ self.video_download_folder_filechooser_button.set_action(
+ gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ self.video_download_folder_filechooser_button.connect("selection-changed",
+ self.on_video_download_folder_filechooser_button_selection_changed)
+
+ self.video_download_folder_table.attach(
+ self.video_download_folder_filechooser_button,
+ 2, 3, 2, 3, yoptions = gtk.SHRINK)
+ self.video_download_folder_filechooser_button.show()
+ self._setupVideoSubfolderTable()
+ self.updateVideoDownloadFolderExample()
def _setupImageRenameTab(self):
- self.rename_table = ImageRenameTable(self, self.rename_scrolledwindow)
+ self.rename_table = ImageRenameTable(self, self.rename_scrolledwindow)
self.rename_table_vbox.pack_start(self.rename_table)
self.rename_table.show_all()
self.original_name_label.set_markup("<i>%s</i>" % self.sampleImageName)
self.updateImageRenameExample()
+ def _setupVideoRenameTab(self):
+
+ self.video_rename_table = VideoRenameTable(self, self.video_rename_scrolledwindow)
+ self.video_rename_table_vbox.pack_start(self.video_rename_table)
+ self.video_rename_table.show_all()
+ self.video_original_name_label.set_markup("<i>%s</i>" % self.sampleVideoName)
+ self.updateVideoRenameExample()
+
def _setupRenameOptionsTab(self):
# sequence numbers
@@ -848,8 +975,9 @@ class PreferencesDialog(gnomeglade.Component):
gtk.STOCK_CLEAR,
gtk.ICON_SIZE_BUTTON))
def _setupDeviceTab(self):
+
self.device_location_filechooser_button = gtk.FileChooserButton(
- _("Select an image folder"))
+ _("Select a folder containing %(file_types)s") % {'file_types':self.file_types})
self.device_location_filechooser_button.set_current_folder(
self.prefs.device_location)
self.device_location_filechooser_button.set_action(
@@ -871,7 +999,7 @@ class PreferencesDialog(gnomeglade.Component):
def _setupBackupTab(self):
self.backup_folder_filechooser_button = gtk.FileChooserButton(
- _("Select a folder in which to backup images"))
+ _("Select a folder in which to backup %(file_types)s") % {'file_types':self.file_types})
self.backup_folder_filechooser_button.set_current_folder(
self.prefs.backup_location)
self.backup_folder_filechooser_button.set_action(
@@ -879,9 +1007,10 @@ class PreferencesDialog(gnomeglade.Component):
self.backup_folder_filechooser_button.connect("selection-changed",
self.on_backup_folder_filechooser_button_selection_changed)
self.backup_table.attach(self.backup_folder_filechooser_button,
- 3, 4, 7, 8, yoptions = gtk.SHRINK)
+ 3, 4, 8, 9, yoptions = gtk.SHRINK)
self.backup_folder_filechooser_button.show()
self.backup_identifier_entry.set_text(self.prefs.backup_identifier)
+ self.video_backup_identifier_entry.set_text(self.prefs.video_backup_identifier)
#setup controls for manipulating sensitivity
self._backupControls0 = [self.auto_detect_backup_checkbutton,
@@ -899,6 +1028,9 @@ class PreferencesDialog(gnomeglade.Component):
self.backup_location_explanation_label]
self._backupControls = self._backupControls0 + self._backupControls1 + \
self._backupControls2
+
+ self._backupVideoControls = [self.video_backup_identifier_label,
+ self.video_backup_identifier_entry]
#assign values to checkbuttons only when other controls
#have been setup, because their toggle signal is activated
@@ -944,15 +1076,12 @@ class PreferencesDialog(gnomeglade.Component):
else:
self.backup_duplicate_skip_radiobutton.set_active(True)
- def updateImageRenameExample(self):
- """
- Displays example image name to the user
- """
-
- if hasattr(self, 'rename_table'):
- self.rename_table.updateExampleJobCode()
- name, problem = self.rename_table.prefsFactory.generateNameUsingPreferences(
- self.sampleImage, self.sampleImageName,
+
+ def updateExampleFileName(self, display_table, rename_table, sample, sampleName, example_label):
+ if hasattr(self, display_table):
+ rename_table.updateExampleJobCode()
+ name, problem = rename_table.prefsFactory.generateNameUsingPreferences(
+ sample, sampleName,
self.prefs.strip_characters, sequencesPreliminary=False)
else:
name = problem = ''
@@ -963,33 +1092,54 @@ class PreferencesDialog(gnomeglade.Component):
if problem:
text += "\n"
# Translators: please do not modify or leave out html formatting tags like <i> and <b>. These are used to format the text the users sees
- text += _("<i><b>Warning:</b> There is insufficient image metadata to fully generate the name. Please use other renaming options.</i>")
+ text += _("<i><b>Warning:</b> There is insufficient metadata to fully generate the name. Please use other renaming options.</i>")
- self.new_name_label.set_markup(text)
+ example_label.set_markup(text)
+
+ def updateImageRenameExample(self):
+ """
+ Displays example image name to the user
+ """
+ self.updateExampleFileName('rename_table', self.rename_table, self.sampleImage, self.sampleImageName, self.new_name_label)
+
+
+ def updateVideoRenameExample(self):
+ """
+ Displays example video name to the user
+ """
+ self.updateExampleFileName('video_rename_table', self.video_rename_table, self.sampleVideo, self.sampleVideoName, self.video_new_name_label)
- def updateDownloadFolderExample(self):
+ def updateDownloadFolderExample(self, display_table, subfolder_table, download_folder, sample, sampleName, example_download_path_label, subfolder_warning_label):
"""
Displays example subfolder name(s) to the user
"""
- if hasattr(self, 'subfolder_table'):
- self.subfolder_table.updateExampleJobCode()
- path, problem = self.subfolder_table.prefsFactory.generateNameUsingPreferences(
- self.sampleImage, self.sampleImageName,
+ if hasattr(self, display_table):
+ subfolder_table.updateExampleJobCode()
+ path, problem = subfolder_table.prefsFactory.generateNameUsingPreferences(
+ sample, sampleName,
self.prefs.strip_characters)
else:
path = problem = ''
- text = os.path.join(self.prefs.download_folder, path)
+ text = os.path.join(download_folder, path)
# since this is markup, escape it
path = common.escape(text)
if problem:
- warning = _("<i><b>Warning:</b> There is insufficient image metadata to fully generate subfolders. Please use other subfolder naming options.</i>" )
+ warning = _("<i><b>Warning:</b> There is insufficient metadata to fully generate subfolders. Please use other subfolder naming options.</i>" )
else:
warning = ""
# Translators: you should not modify or leave out the %s. This is a code used by the programming language python to insert a value that thes user will see
- self.example_download_path_label.set_markup(_("<i>Example: %s</i>") % text)
- self.subfolder_warning_label.set_markup(warning)
+ example_download_path_label.set_markup(_("<i>Example: %s</i>") % text)
+ subfolder_warning_label.set_markup(warning)
+
+ def updatePhotoDownloadFolderExample(self):
+ if hasattr(self, 'subfolder_table'):
+ self.updateDownloadFolderExample('subfolder_table', self.subfolder_table, self.prefs.download_folder, self.sampleImage, self.sampleImageName, self.example_photo_download_path_label, self.photo_subfolder_warning_label)
+
+ def updateVideoDownloadFolderExample(self):
+ if hasattr(self, 'video_subfolder_table'):
+ self.updateDownloadFolderExample('video_subfolder_table', self.video_subfolder_table, self.prefs.video_download_folder, self.sampleVideo, self.sampleVideoName, self.example_video_download_path_label, self.video_subfolder_warning_label)
def on_hour_spinbutton_value_changed(self, spinbutton):
hour = spinbutton.get_value_as_int()
@@ -1037,25 +1187,42 @@ class PreferencesDialog(gnomeglade.Component):
sequences.setStoredSequenceNo(v)
self.updateImageRenameExample()
+ def _updateSubfolderPrefOnError(self, newPrefList):
+ self.prefs.subfolder = newPrefList
+
+ def _updateVideoSubfolderPrefOnError(self, newPrefList):
+ self.prefs.video_subfolder = newPrefList
+
+
+ def checkSubfolderValuesValidOnExit(self, usersPrefList, updatePrefFunction, filetype, defaultPrefList):
+ """
+ Checks that the user has not entered in any inappropriate values
+
+ If they have, filters out bad values and warns the user
+ """
+ filtered, prefList = rn.filterSubfolderPreferences(usersPrefList)
+ if filtered:
+ cmd_line(_("The %(filetype)s subfolder preferences had some unnecessary values removed.") % {'filetype': filetype})
+ if prefList:
+ updatePrefFunction(prefList)
+ else:
+ #Preferences list is now empty
+ msg = _("The %(filetype)s subfolder preferences entered are invalid and cannot be used.\nThey will be reset to their default values.") % {'filetype': filetype}
+ sys.stderr.write(msg + "\n")
+ misc.run_dialog(PROGRAM_NAME, msg)
+ updatePrefFunction(self.prefs.get_default(defaultPrefList))
+
def on_response(self, dialog, arg):
if arg == gtk.RESPONSE_HELP:
webbrowser.open("http://www.damonlynch.net/rapid/documentation")
else:
# arg==gtk.RESPONSE_CLOSE, or the user hit the 'x' to close the window
self.prefs.backup_identifier = self.backup_identifier_entry.get_property("text")
+ self.prefs.video_backup_identifier = self.video_backup_identifier_entry.get_property("text")
#check subfolder preferences for bad values
- filtered, prefList = rn.filterSubfolderPreferences(self.prefs.subfolder)
- if filtered:
- cmd_line(_("The subfolder preferences had some unnecessary values removed."))
- if prefList:
- self.prefs.subfolder = prefList
- else:
- #Preferences list is now empty
- msg = _("The subfolder preferences entered are invalid and cannot be used.\nThey will be reset to their default values.")
- sys.stderr.write(msg + "\n")
- misc.run_dialog(PROGRAM_NAME, msg)
- self.prefs.subfolder = self.prefs.get_default("subfolder")
+ self.checkSubfolderValuesValidOnExit(self.prefs.subfolder, self._updateSubfolderPrefOnError, _("photo"), "subfolder")
+ self.checkSubfolderValuesValidOnExit(self.prefs.video_subfolder, self._updateVideoSubfolderPrefOnError, _("video"), "video_subfolder")
self.widget.destroy()
self.parentApp.preferencesDialogDisplayed = False
@@ -1186,7 +1353,8 @@ class PreferencesDialog(gnomeglade.Component):
def on_strip_characters_checkbutton_toggled(self, check_button):
self.prefs.strip_characters = check_button.get_active()
self.updateImageRenameExample()
- self.updateDownloadFolderExample()
+ self.updatePhotoDownloadFolderExample()
+ self.updateVideoDownloadFolderExample()
def on_indicate_download_error_checkbutton_toggled(self, check_button):
self.prefs.indicate_download_error = check_button.get_active()
@@ -1223,13 +1391,12 @@ class PreferencesDialog(gnomeglade.Component):
"""
if not self.backup_checkbutton.get_active():
- for c in self._backupControls:
+ for c in self._backupControls + self._backupVideoControls:
c.set_sensitive(False)
else:
for c in self._backupControls0:
c.set_sensitive(True)
-
self.updateBackupControlsAuto()
def updateBackupControlsAuto(self):
@@ -1242,12 +1409,43 @@ class PreferencesDialog(gnomeglade.Component):
c.set_sensitive(True)
for c in self._backupControls2:
c.set_sensitive(False)
+ for c in self._backupVideoControls:
+ c.set_sensitive(False)
+ if DOWNLOAD_VIDEO:
+ for c in self._backupVideoControls:
+ c.set_sensitive(True)
else:
for c in self._backupControls1:
c.set_sensitive(False)
for c in self._backupControls2:
c.set_sensitive(True)
+ if DOWNLOAD_VIDEO:
+ for c in self._backupVideoControls:
+ c.set_sensitive(False)
+
+ def disableVideoControls(self):
+ """
+ Disables video preferences if video downloading is disabled
+ (probably because the appropriate libraries to enable
+ video metadata extraction are not installed)
+ """
+ controls = [self.example_video_filename_label,
+ self.original_video_filename_label,
+ self.new_video_filename_label,
+ self.video_new_name_label,
+ self.video_original_name_label,
+ self.video_rename_scrolledwindow,
+ self.video_folders_hbox,
+ self.video_backup_identifier_label,
+ self.video_backup_identifier_entry
+ ]
+ for c in controls:
+ c.set_sensitive(False)
+ self.videos_cannot_be_downloaded_label.show()
+ self.folder_videos_cannot_be_downloaded_label.show()
+ self.folder_videos_cannot_be_downloaded_hbox.show()
+
def on_auto_detect_backup_checkbutton_toggled(self, widget):
self.prefs.backup_device_autodetection = widget.get_active()
self.updateBackupControlsAuto()
@@ -1258,25 +1456,53 @@ class PreferencesDialog(gnomeglade.Component):
def on_backup_identifier_entry_changed(self, widget):
self.updateBackupExample()
+
+ def on_video_backup_identifier_entry_changed(self, widget):
+ self.updateBackupExample()
def on_backup_scan_folder_on_entry_changed(self, widget):
self.updateBackupExample()
def updateBackupExample(self):
# Translators: this value is used as an example device when automatic backup device detection is enabled. You should translate this.
- path = os.path.join(config.MEDIA_LOCATION, _("externaldrive1"))
+ drive1 = os.path.join(config.MEDIA_LOCATION, _("externaldrive1"))
# Translators: this value is used as an example device when automatic backup device detection is enabled. You should translate this.
- path2 = os.path.join(config.MEDIA_LOCATION, _("externaldrive2"))
+ drive2 = os.path.join(config.MEDIA_LOCATION, _("externaldrive2"))
- path = os.path.join(path, self.backup_identifier_entry.get_text())
- path2 = os.path.join(path2, self.backup_identifier_entry.get_text())
+ path = os.path.join(drive1, self.backup_identifier_entry.get_text())
+ path2 = os.path.join(drive2, self.backup_identifier_entry.get_text())
+ path3 = os.path.join(drive2, self.video_backup_identifier_entry.get_text())
path = common.escape(path)
path2 = common.escape(path2)
- self.example_backup_path_label.set_markup("<i>%s</i>\n<i>%s</i>" % (path,
- path2))
+ path3 = common.escape(path3)
+ if DOWNLOAD_VIDEO:
+ example = "<i>%s</i>\n<i>%s</i>\n<i>%s</i>" % (path, path2, path3)
+ else:
+ example = "<i>%s</i>\n<i>%s</i>" % (path, path2)
+ self.example_backup_path_label.set_markup(example)
-
+def file_types_by_number(noImages, noVideos):
+ """
+ returns a string to be displayed to the user that can be used
+ to show if a value refers to photos or videos or both, or just one
+ of each
+ """
+ if (noVideos > 0) and (noImages > 0):
+ v = _('photos and videos')
+ elif (noVideos == 0) and (noImages == 0):
+ v = _('photos or videos')
+ elif noVideos > 0:
+ if noVideos > 1:
+ v = _('videos')
+ else:
+ v = _('video')
+ else:
+ if noImages > 1:
+ v = _('photos')
+ else:
+ v = _('photo')
+ return v
class CopyPhotos(Thread):
"""Copies photos from source to destination, backing up if needed"""
@@ -1324,8 +1550,8 @@ class CopyPhotos(Thread):
media_collection_treeview.addCard(thread_id, self.cardMedia.prettyName(),
'', 0, progress=0.0,
# This refers to when a device like a hard drive is having its contents scanned,
- # looking for images. It is visible initially in the progress bar for each device
- # (which normally holds "x of y images copied").
+ # looking for photos or videos. It is visible initially in the progress bar for each device
+ # (which normally holds "x of y photos").
# It maybe displayed only briefly if the contents of the device being scanned is small.
progressBarText=_('scanning...'))
@@ -1347,29 +1573,37 @@ class CopyPhotos(Thread):
"""
Setup thread so that user preferences are handled
"""
+
+ def checkPrefs(prefsFactory):
+ try:
+ prefsFactory.checkPrefsForValidity()
+ except (rn.PrefValueInvalidError, rn.PrefLengthError,
+ rn.PrefValueKeyComboError, rn.PrefKeyError), e:
+ if notifyOnError:
+ self.handlePreferencesError(e, prefsFactory)
+ raise rn.PrefError
+
self.prefs = self.parentApp.prefs
+
+
+ #Image and Video filename preferences
self.imageRenamePrefsFactory = rn.ImageRenamePreferences(self.prefs.image_rename, self,
self.fileSequenceLock, sequences)
- try:
- self.imageRenamePrefsFactory.checkPrefsForValidity()
- except (rn.PrefValueInvalidError, rn.PrefLengthError,
- rn.PrefValueKeyComboError, rn.PrefKeyError), e:
- if notifyOnError:
- self.handlePreferencesError(e, self.imageRenamePrefsFactory)
- raise rn.PrefError
-
+ checkPrefs(self.imageRenamePrefsFactory)
+
+ self.videoRenamePrefsFactory = rn.VideoRenamePreferences(self.prefs.video_rename, self,
+ self.fileSequenceLock, sequences)
+ checkPrefs(self.videoRenamePrefsFactory)
+
+ #Image and Video subfolder preferences
- self.subfolderPrefsFactory = rn.SubfolderPreferences(
- self.prefs.subfolder, self)
- try:
- self.subfolderPrefsFactory.checkPrefsForValidity()
- except (rn.PrefValueInvalidError, rn.PrefLengthError,
- rn.PrefValueKeyComboError, rn.PrefKeyError), e:
- if notifyOnError:
- self.handlePreferencesError(e, self.subfolderPrefsFactory)
- raise rn.PrefError
-
+ self.subfolderPrefsFactory = rn.SubfolderPreferences(self.prefs.subfolder, self)
+ checkPrefs(self.subfolderPrefsFactory)
+
+ self.videoSubfolderPrefsFactory = rn.VideoSubfolderPreferences(self.prefs.video_subfolder, self)
+ checkPrefs(self.videoSubfolderPrefsFactory)
+
# copy this variable, as it is used heavily in the loop
# and it is perhaps relatively expensive to read
self.stripCharacters = self.prefs.strip_characters
@@ -1409,6 +1643,32 @@ class CopyPhotos(Thread):
3.b if so, user preferences determine whether it should be overwritten or not
"""
+ def checkDownloadPath(path):
+ """
+ Checks to see if download folder exists.
+
+ Creates it if it does not exist.
+
+ Returns False if the path could not be created.
+ """
+
+ try:
+ if not os.path.isdir(path):
+ os.makedirs(path)
+ return True
+
+ except:
+ if notifyOnError:
+ display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
+ msg = _("The following download path could not be created:\n")
+ msg += _("%(path)s: ") % {'path': path}
+ logError(config.CRITICAL_ERROR, _("Download cannot proceed"), msg)
+ cmd_line(_("Download cannot proceed"))
+ cmd_line(msg)
+ display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
+ display_queue.close("rw")
+ return False
+
def getPrefs(notifyOnError):
try:
self.initializeFromPrefs(notifyOnError)
@@ -1424,12 +1684,23 @@ class CopyPhotos(Thread):
display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
display_queue.close("rw")
return False
+
def scanMedia():
- images = []
- imageSizeSum = 0
- for root, dirs, files in os.walk(self.cardMedia.getPath()):
- for name in files:
+ def downloadFile(name):
+ isImage = media.isImage(name)
+ isVideo = media.isVideo(name)
+ download = (DOWNLOAD_VIDEO and (isImage or isVideo) or
+ ((not DOWNLOAD_VIDEO) and isImage))
+ return (download, isImage, isVideo)
+
+ def gio_scan(path, fileSizeSum):
+ """recursive function to scan a directory and its subdirectories
+ for photos and possibly videos"""
+
+ children = path.enumerate_children('standard::name,standard::type,standard::size,time::modified')
+
+ for child in children:
if not self.running:
self.lock.acquire()
self.running = True
@@ -1438,44 +1709,105 @@ class CopyPhotos(Thread):
self.running = False
display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
display_queue.close("rw")
- return
-
- if media.isImage(name):
- image = os.path.join(root, name)
- size = os.path.getsize(image)
- modificationTime = os.path.getmtime(image)
- images.append((name, root, size, modificationTime),)
- imageSizeSum += size
- images.sort(key=operator.itemgetter(3))
- noImages = len(images)
+ return None
+
+ if child.get_file_type() == gio.FILE_TYPE_DIRECTORY:
+ fileSizeSum = gio_scan(path.get_child(child.get_name()), fileSizeSum)
+ if fileSizeSum == None:
+ # this value will be None only if the thread is exiting
+ return None
+ elif child.get_file_type() == gio.FILE_TYPE_REGULAR:
+ name = child.get_name()
+ download, isImage, isVideo = downloadFile(name)
+ if download:
+ size = child.get_size()
+ imagesAndVideos.append((name, path.get_path(), size, child.get_modification_time()),)
+ fileSizeSum += size
+ if isVideo:
+ self.noVideos += 1
+ else:
+ self.noImages += 1
+ return fileSizeSum
+
+
+ imagesAndVideos = []
+ fileSizeSum = 0
+ self.noVideos = 0
+ self.noImages = 0
+
+ if not using_gio or not self.cardMedia.volume:
+ for root, dirs, files in os.walk(self.cardMedia.getPath()):
+ for name in files:
+ if not self.running:
+ self.lock.acquire()
+ self.running = True
+
+ if not self.ctrl:
+ self.running = False
+ display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
+ display_queue.close("rw")
+ return
+
+
+ download, isImage, isVideo = downloadFile(name)
+ if download:
+ image = os.path.join(root, name)
+ size = os.path.getsize(image)
+ modificationTime = os.path.getmtime(image)
+ imagesAndVideos.append((name, root, size, modificationTime),)
+ fileSizeSum += size
+ if isVideo:
+ self.noVideos += 1
+ else:
+ self.noImages += 1
+
+ else:
+ # using gio and have a volume
+ # make call to recursive function to scan volume
+ fileSizeSum = gio_scan(self.cardMedia.volume.volume.get_root(), fileSizeSum)
+ if fileSizeSum == None:
+ # thread exiting
+ return
+
+ imagesAndVideos.sort(key=operator.itemgetter(3))
+ noFiles = len(imagesAndVideos)
self.scanComplete = True
- if noImages:
- self.cardMedia.setMedia(images, imageSizeSum, noImages)
+ self.display_file_types = file_types_by_number(self.noImages, self.noVideos)
+
+ if DOWNLOAD_VIDEO:
+ self.types_searched_for = _('photos or videos')
+ else:
+ self.types_searched_for = _('photos')
+
+
+ if noFiles:
+ self.cardMedia.setMedia(imagesAndVideos, fileSizeSum, noFiles)
# Translators: as already, mentioned the %s value should not be modified or left out. It may be moved if necessary.
- # It refers to the actual number of images that can be copied. For example, the user might see the following:
- # '0 of 512 images copied'.
+ # It refers to the actual number of photos that can be copied. For example, the user might see the following:
+ # '0 of 512 photos' or '0 of 10 videos' or '0 of 202 photos and videos'.
# This particular text is displayed to the user before the download has started.
- display = _("0 of %s images copied") % noImages
- display_queue.put((media_collection_treeview.updateCard, (self.thread_id, self.cardMedia.sizeOfImages(), noImages)))
+ display = _("0 of %(number)s %(filetypes)s") % {'number':noFiles, 'filetypes':self.display_file_types}
+ display_queue.put((media_collection_treeview.updateCard, (self.thread_id, self.cardMedia.sizeOfImagesAndVideos(), noFiles)))
display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, 0.0, display, 0)))
- display_queue.put((self.parentApp.timeRemaining.add, (self.thread_id, imageSizeSum)))
+ display_queue.put((self.parentApp.timeRemaining.add, (self.thread_id, fileSizeSum)))
display_queue.put((self.parentApp.setDownloadButtonSensitivity, ()))
# Translators: as you have already seen, the text can contain values that should not be modified or left out by you, for example %s.
# This text is another example of that, but it is is a little more complex. Here there are two values which will be displayed
- # to the user when they run the program, signifying the number of images found, and the device they were found on.
+ # to the user when they run the program, signifying the number of photos found, and the device they were found on.
# %(number)s should be left exactly as is: 'number' should not be translated. The same applies to %(device)s: 'device' should
# not be translated. Generally speaking, if translating the sentence requires it, you can move items like '%(xyz)s' around
# in a sentence, but you should never modify them or leave them out.
- cmd_line(_("Device scan complete: found %(number)s images on %(device)s") %
- {'number': noImages, 'device': self.cardMedia.prettyName(limit=0)})
+ cmd_line(_("Device scan complete: found %(number)s %(filetypes)s on %(device)s") %
+ {'number': noFiles, 'filetypes':self.display_file_types,
+ 'device': self.cardMedia.prettyName(limit=0)})
return True
else:
# it might be better to display "0 of 0" here
display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
- cmd_line(_("Device scan complete: no images found on %s") % self.cardMedia.prettyName(limit=0))
+ cmd_line(_("Device scan complete: no %(filetypes)s found on %(device)s") % {'device':self.cardMedia.prettyName(limit=0), 'filetypes':self.types_searched_for})
return False
def cleanUp():
@@ -1484,13 +1816,16 @@ class CopyPhotos(Thread):
early or when it has completed its run.
"""
- # possibly delete any lingering files
- tf = os.listdir(tempWorkingDir)
- if tf:
- for f in tf:
- os.remove(os.path.join(tempWorkingDir, f))
-
- os.rmdir(tempWorkingDir)
+
+ for tempWorkingDir in (videoTempWorkingDir, photoTempWorkingDir):
+ if tempWorkingDir:
+ # possibly delete any lingering files
+ tf = os.listdir(tempWorkingDir)
+ if tf:
+ for f in tf:
+ os.remove(os.path.join(tempWorkingDir, f))
+
+ os.rmdir(tempWorkingDir)
def logError(severity, problem, details, resolution=None):
@@ -1502,136 +1837,186 @@ class CopyPhotos(Thread):
self.noErrors += 1
- def checkProblemWithImageNameGeneration(newName, destination, image, problem):
+ def checkProblemWithNameGeneration(newName, destination, source, problem, filetype):
if not newName:
# a serious problem - a filename should never be blank!
logError(config.SERIOUS_ERROR,
- _("Image filename could not be generated"),
+ _("%(filetype)s filename could not be generated") % {'filetype': filetype},
# '%(source)s' and '%(problem)s' are two more examples of text that should not be modified or left out
- _("Source: %(source)s\nProblem: %(problem)s") % {'source': image, 'problem': problem},
- IMAGE_SKIPPED)
+ _("Source: %(source)s\nProblem: %(problem)s") % {'source': source, 'problem': problem},
+ fileSkippedDisplay)
elif problem:
logError(config.WARNING,
- _("Image filename could not be properly generated. Check to ensure there is sufficient image metadata."),
+ _("%(filetype)s filename could not be properly generated. Check to ensure there is sufficient metadata.") % {'filetype': filetype},
_("Source: %(source)s\nPartially generated filename: %(newname)s\nDestination: %(destination)s\nProblem: %(problem)s") %
- {'source': image, 'destination': destination, 'newname': newName, 'problem': problem})
+ {'source': source, 'destination': destination, 'newname': newName, 'problem': problem})
-
- def imageAlreadyExists(source, destination=None, identifier=None):
- """ Notify the user that the image could not be downloaded because it already exists"""
+ def fileAlreadyExists(source, fileSkippedDisplay, fileAlreadyExistsDisplay, destination=None, identifier=None):
+ """ Notify the user that the photo or video could not be downloaded because it already exists"""
if self.prefs.indicate_download_error:
if source and destination and identifier:
- logError(config.SERIOUS_ERROR, IMAGE_ALREADY_EXISTS,
+ logError(config.SERIOUS_ERROR, fileAlreadyExistsDisplay,
_("Source: %(source)s\nDestination: %(destination)s")
- % {'source': image, 'destination': newFile},
+ % {'source': source, 'destination': newFile},
_("Unique identifier '%s' added") % identifier)
elif source and destination:
- logError(config.SERIOUS_ERROR, IMAGE_ALREADY_EXISTS,
+ logError(config.SERIOUS_ERROR, fileAlreadyExistsDisplay,
_("Source: %(source)s\nDestination: %(destination)s")
% {'source': source, 'destination': destination},
- IMAGE_SKIPPED)
+ fileSkippedDisplay)
else:
- logError(config.SERIOUS_ERROR, IMAGE_ALREADY_EXISTS,
+ logError(config.SERIOUS_ERROR, fileAlreadyExistsDisplay,
_("Source: %(source)s")
% {'source': source},
- IMAGE_SKIPPED)
+ fileSkippedDisplay)
- def downloadCopyingError(source, destination, errno, strerror):
- """Notify the user that an error occurred when coyping an image"""
- logError(config.SERIOUS_ERROR, _('Download copying error'),
- _("Source: %(source)s\nDestination: %(destination)s\nError: %(errorno)s %(strerror)s")
- % {'source': source, 'destination': destination, 'errorno': errno, 'strerror': strerror},
- _('The image was not copied.'))
+ def downloadCopyingError(source, destination, filetype, errno=None, strerror=None):
+ """Notify the user that an error occurred when coyping an photo or video"""
+ if errno != None and strerror != None:
+ logError(config.SERIOUS_ERROR, _('Download copying error'),
+ _("Source: %(source)s\nDestination: %(destination)s\nError: %(errorno)s %(strerror)s")
+ % {'source': source, 'destination': destination, 'errorno': errno, 'strerror': strerror},
+ _('The %(filetype)s was not copied.') % {'filetype': filetype})
+ else:
+ logError(config.SERIOUS_ERROR, _('Download copying error'),
+ _("Source: %(source)s\nDestination: %(destination)s")
+ % {'source': source, 'destination': destination},
+ _('The %(filetype)s was not copied.') % {'filetype': filetype})
+
def sameFileNameDifferentExif(image1, image1_date_time, image1_subseconds, image2, image2_date_time, image2_subseconds):
- logError(config.WARNING, _('Images detected with the same filenames, but taken at different times:'),
- _("First image: %(image1)s %(image1_date_time)s:%(image1_subseconds)s\nSecond image: %(image2)s %(image2_date_time)s:%(image2_subseconds)s") %
+ logError(config.WARNING, _('Photos detected with the same filenames, but taken at different times:'),
+ _("First photo: %(image1)s %(image1_date_time)s:%(image1_subseconds)s\nSecond photo: %(image2)s %(image2_date_time)s:%(image2_subseconds)s") %
{'image1': image1, 'image1_date_time': image1_date_time, 'image1_subseconds': image1_subseconds,
'image2': image2, 'image2_date_time': image2_date_time, 'image2_subseconds': image2_subseconds})
- def generateSubfolderAndFileName(image, name, needMetaDataToCreateUniqueImageName,
- needMetaDataToCreateUniqueSubfolderName):
- skipImage = alreadyDownloaded = False
+ def generateSubfolderAndFileName(fullFileName, name, needMetaDataToCreateUniqueImageName,
+ needMetaDataToCreateUniqueSubfolderName, fallback_date):
+ """
+ Generates subfolder and file names for photos and videos
+ """
+
+ skipFile = alreadyDownloaded = False
sequence_to_use = None
- try:
- imageMetadata = metadata.MetaData(image)
- except IOError:
- logError(config.CRITICAL_ERROR, _("Could not open image"),
- _("Source: %s") % image,
- IMAGE_SKIPPED)
- skipImage = True
- imageMetadata = newName = newFile = path = subfolder = None
- else:
+
+ if not self.isImage:
+ # file is a video file
+ fileRenameFactory = self.videoRenamePrefsFactory
+ subfolderFactory = self.videoSubfolderPrefsFactory
try:
- # this step can fail if the source image is corrupt
- imageMetadata.readMetadata()
+ # this step immedidately reads the metadata from the video file
+ # (which is different than pyexiv2)
+ fileMetadata = videometadata.VideoMetaData(fullFileName)
except:
- skipImage = True
-
- if not skipImage:
- if not imageMetadata.exifKeys() and (needMetaDataToCreateUniqueSubfolderName or
- (needMetaDataToCreateUniqueImageName and
- not addUniqueIdentifier)):
- skipImage = True
-
- if skipImage:
- logError(config.SERIOUS_ERROR, _("Image has no metadata"),
- _("Metadata is essential for generating subfolders / image names.\nSource: %s") % image,
- IMAGE_SKIPPED)
- newName = newFile = path = subfolder = None
+ logError(config.CRITICAL_ERROR, _("Could not open %(filetype)s") % {'filetype': fileBeingDownloadedDisplay},
+ _("Source: %s") % fullFileName,
+ fileSkippedDisplay)
+ skipFile = True
+ fileMetadata = newName = newFile = path = subfolder = sequence_to_use = None
+ return (skipFile, fileMetadata, newName, newFile, path, subfolder, sequence_to_use)
+ else:
+ # file is an photo
+ fileRenameFactory = self.imageRenamePrefsFactory
+ subfolderFactory = self.subfolderPrefsFactory
+ try:
+ fileMetadata = metadata.MetaData(fullFileName)
+ except IOError:
+ logError(config.CRITICAL_ERROR, _("Could not open %(filetype)s") % {'filetype': fileBeingDownloadedDisplay},
+ _("Source: %s") % fullFileName,
+ fileSkippedDisplay)
+ skipFile = True
+ fileMetadata = newName = newFile = path = subfolder = sequence_to_use = None
+ return (skipFile, fileMetadata, newName, newFile, path, subfolder, sequence_to_use)
else:
- subfolder, problem = self.subfolderPrefsFactory.generateNameUsingPreferences(
- imageMetadata, name,
- self.stripCharacters)
-
- if problem:
- logError(config.WARNING,
- _("Subfolder name could not be properly generated. Check to ensure there is sufficient image metadata."),
- _("Subfolder: %(subfolder)s\nImage: %(image)s\nProblem: %(problem)s") %
- {'subfolder': subfolder, 'image': image, 'problem': problem})
-
- if self.prefs.synchronize_raw_jpg and usesSequenceElements:
- image_name, image_ext = os.path.splitext(name)
- with self.downloadedFilesLock:
- i, sequence_to_use = downloaded_files.matching_pair(image_name, image_ext, imageMetadata.dateTime(), imageMetadata.subSeconds())
- if i == -1:
- # this exact file has already been downloaded (same extension, same filename, and same exif date time subsecond info)
- if not addUniqueIdentifier:
- # there is no point to download it, as there is no way a unique filename will be generated
- alreadyDownloaded = skipImage = True
- elif i == -99:
- i1_ext, i1_date_time, i1_subseconds = downloaded_files.extExifDateTime(image_name)
- sameFileNameDifferentExif("%s%s" % (image_name, i1_ext), i1_date_time, i1_subseconds, name, imageMetadata.dateTime(), imageMetadata.subSeconds())
-
-
- # pass the subfolder the image will go into, as this is needed to determine subfolder sequence numbers
- # indicate that sequences chosen should be queued
+ try:
+ # this step can fail if the source photo is corrupt
+ fileMetadata.read()
+ except:
+ skipFile = True
+
- if not skipImage or alreadyDownloaded:
- newName, problem = self.imageRenamePrefsFactory.generateNameUsingPreferences(
- imageMetadata, name, self.stripCharacters, subfolder,
+ if not skipFile:
+ if self.isImage and not fileMetadata.rpd_keys() and (needMetaDataToCreateUniqueSubfolderName or
+ (needMetaDataToCreateUniqueImageName and
+ not addUniqueIdentifier)):
+ skipFile = True
+
+ #TODO similar checking for video
+
+ if skipFile:
+ logError(config.SERIOUS_ERROR, _("%(filetype)s has no metadata") % {'filetype': fileBeingDownloadedDisplayCap},
+ _("Metadata is essential for generating subfolder and/or file names.\nSource: %s") % fullFileName,
+ fileSkippedDisplay)
+ newName = newFile = path = subfolder = None
+ else:
+ # attempt to generate a subfolder name
+ subfolder, problem = subfolderFactory.generateNameUsingPreferences(
+ fileMetadata, name,
+ self.stripCharacters, fallback_date = fallback_date)
+
+ if problem:
+ logError(config.WARNING,
+ _("Subfolder name could not be properly generated. Check to ensure there is sufficient metadata."),
+ _("Subfolder: %(subfolder)s\nFile: %(file)s\nProblem: %(problem)s") %
+ {'subfolder': subfolder, 'file': fullFileName, 'problem': problem})
+
+ if self.prefs.synchronize_raw_jpg and usesImageSequenceElements and self.isImage:
+ #synchronizing RAW and JPEG only applies to photos, not videos
+ image_name, image_ext = os.path.splitext(name)
+ with self.downloadedFilesLock:
+ i, sequence_to_use = downloaded_files.matching_pair(image_name, image_ext, fileMetadata.dateTime(), fileMetadata.subSeconds())
+ if i == -1:
+ # this exact file has already been downloaded (same extension, same filename, and same exif date time subsecond info)
+ if not addUniqueIdentifier:
+ # there is no point to download it, as there is no way a unique filename will be generated
+ alreadyDownloaded = skipFile = True
+ elif i == -99:
+ i1_ext, i1_date_time, i1_subseconds = downloaded_files.extExifDateTime(image_name)
+ sameFileNameDifferentExif("%s%s" % (image_name, i1_ext), i1_date_time, i1_subseconds, name, fileMetadata.dateTime(), fileMetadata.subSeconds())
+
+
+ # pass the subfolder the image will go into, as this is needed to determine subfolder sequence numbers
+ # indicate that sequences chosen should be queued
+
+ # TODO check 'or alreadyDownloaded' is meant to be here
+ if not (skipFile or alreadyDownloaded):
+ newName, problem = fileRenameFactory.generateNameUsingPreferences(
+ fileMetadata, name, self.stripCharacters, subfolder,
sequencesPreliminary = True,
- sequence_to_use = sequence_to_use)
-
- path = os.path.join(baseDownloadDir, subfolder)
- newFile = os.path.join(path, newName)
-
- if not newName:
- skipImage = True
- if not alreadyDownloaded:
- checkProblemWithImageNameGeneration(newName, path, image, problem)
- else:
- imageAlreadyExists(image, newFile)
- newName = newFile = path = subfolder = None
+ sequence_to_use = sequence_to_use,
+ fallback_date = fallback_date)
+
+ path = os.path.join(baseDownloadDir, subfolder)
+ newFile = os.path.join(path, newName)
+
+ if not newName:
+ skipFile = True
+ if not alreadyDownloaded:
+ checkProblemWithNameGeneration(newName, path, fullFileName, problem, fileBeingDownloadedDisplayCap)
+ else:
+ fileAlreadyExists(fullFileName, fileSkippedDisplay, fileAlreadyExistsDisplay, newFile)
+ newName = newFile = path = subfolder = None
- return (skipImage, imageMetadata, newName, newFile, path, subfolder, sequence_to_use)
+ return (skipFile, fileMetadata, newName, newFile, path, subfolder, sequence_to_use)
- def downloadImage(path, newFile, newName, originalName, image, imageMetadata, subfolder, sequence_to_use):
+ def downloadFile(path, newFile, newName, originalName, image, fileMetadata, subfolder, sequence_to_use, modificationTime):
+ """
+ Downloads the photo or video file to the specified subfolder
+ """
+
+ if not self.isImage:
+ renameFactory = self.videoRenamePrefsFactory
+ else:
+ renameFactory = self.imageRenamePrefsFactory
+
+ def progress_callback(self, v):
+ pass
+
try:
- imageDownloaded = False
+ fileDownloaded = False
if not os.path.isdir(path):
os.makedirs(path)
@@ -1644,11 +2029,11 @@ class CopyPhotos(Thread):
nameUniqueBeforeCopy = False
if not addUniqueIdentifier:
downloadNonUniqueFile = False
- if usesSequenceElements and not self.prefs.synchronize_raw_jpg:
- # potentially, a unique image name could still be generated
+ if (usesVideoSequenceElements and not self.isImage) or (usesImageSequenceElements and self.isImage and not self.prefs.synchronize_raw_jpg):
+ # potentially, a unique file name could still be generated
# investigate this possibility
with self.fileSequenceLock:
- for possibleName, problem in self.imageRenamePrefsFactory.generateNameSequencePossibilities(imageMetadata,
+ for possibleName, problem in renameFactory.generateNameSequencePossibilities(fileMetadata,
originalName, self.stripCharacters, subfolder):
if possibleName:
# no need to check for any problems here, it's just a temporary name
@@ -1660,176 +2045,235 @@ class CopyPhotos(Thread):
if not downloadNonUniqueFile:
- imageAlreadyExists(image, newFile)
+ fileAlreadyExists(fullFileName, fileSkippedDisplay, fileAlreadyExistsDisplay, newFile)
+ copy_succeeded = False
if nameUniqueBeforeCopy or downloadNonUniqueFile:
tempWorkingfile = os.path.join(tempWorkingDir, newName)
- shutil.copy2(image, tempWorkingfile)
+ if using_gio:
+ g_dest = gio.File(path=tempWorkingfile)
+ g_src = gio.File(path=fullFileName)
+ if not g_src.copy(g_dest, progress_callback, cancellable=gio.Cancellable()):
+ downloadCopyingError(fullFileName, tempWorkingfile, fileBeingDownloadedDisplay)
+ else:
+ copy_succeeded = True
+ else:
+ shutil.copy2(fullFileName, tempWorkingfile)
+ copy_succeeded = True
- with self.fileRenameLock:
- doRename = True
- if usesSequenceElements:
- with self.fileSequenceLock:
- # get a filename and use this as the "real" filename
- if sequence_to_use is None and self.prefs.synchronize_raw_jpg:
- # must check again, just in case the matching pair has been downloaded in the meantime
- image_name, image_ext = os.path.splitext(originalName)
- with self.downloadedFilesLock:
- i, sequence_to_use = downloaded_files.matching_pair(image_name, image_ext, imageMetadata.dateTime(), imageMetadata.subSeconds())
- if i == -99:
- i1_ext, i1_date_time, i1_subseconds = downloaded_files.extExifDateTime(image_name)
- sameFileNameDifferentExif("%s%s" % (image_name, i1_ext), i1_date_time, i1_subseconds, originalName, imageMetadata.dateTime(), imageMetadata.subSeconds())
-
-
+ if copy_succeeded:
+ with self.fileRenameLock:
+ doRename = True
+ if usesSequenceElements:
+ with self.fileSequenceLock:
+ # get a filename and use this as the "real" filename
+ if sequence_to_use is None and self.prefs.synchronize_raw_jpg and self.isImage:
+ # must check again, just in case the matching pair has been downloaded in the meantime
+ image_name, image_ext = os.path.splitext(originalName)
+ with self.downloadedFilesLock:
+ i, sequence_to_use = downloaded_files.matching_pair(image_name, image_ext, fileMetadata.dateTime(), fileMetadata.subSeconds())
+ if i == -99:
+ i1_ext, i1_date_time, i1_subseconds = downloaded_files.extExifDateTime(image_name)
+ sameFileNameDifferentExif("%s%s" % (image_name, i1_ext), i1_date_time, i1_subseconds, originalName, fileMetadata.dateTime(), fileMetadata.subSeconds())
- newName, problem = self.imageRenamePrefsFactory.generateNameUsingPreferences(
- imageMetadata, originalName, self.stripCharacters, subfolder,
- sequencesPreliminary = False,
- sequence_to_use = sequence_to_use)
- checkProblemWithImageNameGeneration(newName, path, image, problem)
- if not newName:
- # there was a serious error generating the filename
- doRename = False
- else:
- newFile = os.path.join(path, newName)
- # check if the file exists again
- if os.path.exists(newFile):
- if not addUniqueIdentifier:
- doRename = False
- imageAlreadyExists(image, newFile)
- else:
- # add basic suffix to make the filename unique
- name = os.path.splitext(newName)
- suffixAlreadyUsed = True
- while suffixAlreadyUsed:
- if newFile in duplicate_files:
- duplicate_files[newFile] += 1
- else:
- duplicate_files[newFile] = 1
- identifier = '_%s' % duplicate_files[newFile]
- newName = name[0] + identifier + name[1]
- possibleNewFile = os.path.join(path, newName)
- suffixAlreadyUsed = os.path.exists(possibleNewFile)
-
- imageAlreadyExists(image, newFile, identifier)
- newFile = possibleNewFile
-
+
- if doRename:
- os.rename(tempWorkingfile, newFile)
+ newName, problem = renameFactory.generateNameUsingPreferences(
+ fileMetadata, originalName, self.stripCharacters, subfolder,
+ sequencesPreliminary = False,
+ sequence_to_use = sequence_to_use,
+ fallback_date = fallback_date)
+ checkProblemWithNameGeneration(newName, path, fullFileName, problem, fileBeingDownloadedDisplayCap)
+ if not newName:
+ # there was a serious error generating the filename
+ doRename = False
+ else:
+ newFile = os.path.join(path, newName)
+ # check if the file exists again
+ if os.path.exists(newFile):
+ if not addUniqueIdentifier:
+ doRename = False
+ fileAlreadyExists(fullFileName, fileSkippedDisplay, fileAlreadyExistsDisplay, newFile)
+ else:
+ # add basic suffix to make the filename unique
+ name = os.path.splitext(newName)
+ suffixAlreadyUsed = True
+ while suffixAlreadyUsed:
+ if newFile in duplicate_files:
+ duplicate_files[newFile] += 1
+ else:
+ duplicate_files[newFile] = 1
+ identifier = '_%s' % duplicate_files[newFile]
+ newName = name[0] + identifier + name[1]
+ possibleNewFile = os.path.join(path, newName)
+ suffixAlreadyUsed = os.path.exists(possibleNewFile)
+
+ fileAlreadyExists(fullFileName, fileSkippedDisplay, fileAlreadyExistsDisplay, newFile, identifier=identifier)
+ newFile = possibleNewFile
- imageDownloaded = True
- if usesSequenceElements:
- if self.prefs.synchronize_raw_jpg:
- name, ext = os.path.splitext(originalName)
- if sequence_to_use is None:
- with self.fileSequenceLock:
- seq = self.imageRenamePrefsFactory.sequences.getFinalSequence()
- else:
- seq = sequence_to_use
- with self.downloadedFilesLock:
- downloaded_files.add_download(name, ext, imageMetadata.dateTime(), imageMetadata.subSeconds(), seq)
-
+ if doRename:
+ if using_gio:
+ g_dest = gio.File(path=newFile)
+ g_src = gio.File(path=tempWorkingfile)
+ if not g_src.move(g_dest, progress_callback, cancellable=gio.Cancellable()):
+ downloadCopyingError(tempWorkingfile, newFile, fileBeingDownloadedDisplay)
+ else:
+ os.rename(tempWorkingfile, newFile)
+
+ fileDownloaded = True
+ if usesImageSequenceElements:
+ if self.prefs.synchronize_raw_jpg and self.isImage:
+ name, ext = os.path.splitext(originalName)
+ if sequence_to_use is None:
+ with self.fileSequenceLock:
+ seq = self.imageRenamePrefsFactory.sequences.getFinalSequence()
+ else:
+ seq = sequence_to_use
+ with self.downloadedFilesLock:
+ downloaded_files.add_download(name, ext, fileMetadata.dateTime(), fileMetadata.subSeconds(), seq)
+
+
+ with self.fileSequenceLock:
+ if sequence_to_use is None:
+ renameFactory.sequences.imageCopySucceeded()
+ if usesStoredSequenceNo:
+ self.prefs.stored_sequence_no += 1
+
with self.fileSequenceLock:
if sequence_to_use is None:
- self.imageRenamePrefsFactory.sequences.imageCopySucceeded()
- if usesStoredSequenceNo:
- self.prefs.stored_sequence_no += 1
-
- with self.fileSequenceLock:
- if sequence_to_use is None:
- if self.prefs.incrementDownloadsToday():
- # A new day, according the user's preferences of what time a day begins, has started
- cmd_line(_("New day has started - resetting 'Downloads Today' sequence number"))
-
- sequences.setDownloadsToday(0)
+ if self.prefs.incrementDownloadsToday():
+ # A new day, according the user's preferences of what time a day begins, has started
+ cmd_line(_("New day has started - resetting 'Downloads Today' sequence number"))
+
+ sequences.setDownloadsToday(0)
except IOError, (errno, strerror):
- downloadCopyingError(image, newFile, errno, strerror)
+ downloadCopyingError(fullFileName, newFile, fileBeingDownloadedDisplay, errno, strerror)
except OSError, (errno, strerror):
- downloadCopyingError(image, newFile, errno, strerror)
+ downloadCopyingError(fullFileName, newFile, fileBeingDownloadedDisplay, errno, strerror)
- if usesSequenceElements:
- if not imageDownloaded and sequence_to_use is None:
+ if usesImageSequenceElements:
+ if not fileDownloaded and sequence_to_use is None:
self.imageRenamePrefsFactory.sequences.imageCopyFailed()
- return (imageDownloaded, newName, newFile)
+ return (fileDownloaded, newName, newFile)
- def backupImage(subfolder, newName, imageDownloaded, newFile, image):
- """ backup image to path(s) chosen by the user
+ def backupFile(subfolder, newName, fileDownloaded, newFile, originalFile):
+ """
+ Backup photo or video to path(s) chosen by the user
there are two scenarios:
- (1) image has just been downloaded and should now be backed up
- (2) image was already downloaded on some previous occassion and should still be backed up, because it hasn't been yet
- (3) image has been backed up already (or at least, a file with the same name already exists)
+ (1) file has just been downloaded and should now be backed up
+ (2) file was already downloaded on some previous occassion and should still be backed up, because it hasn't been yet
+ (3) file has been backed up already (or at least, a file with the same name already exists)
+
+ A backup medium can be used to backup photos or videos, or both.
"""
+ #TODO convert to using GIO
backed_up = False
+ fileNotBackedUpMessageDisplayed = False
try:
- for backupDir in self.parentApp.backupVolumes:
- backupPath = os.path.join(backupDir, subfolder)
- newBackupFile = os.path.join(backupPath, newName)
- copyBackup = True
- if os.path.exists(newBackupFile):
- # not thread safe -- it doesn't need to be, because the file names are at this stage going to be unique
- copyBackup = self.prefs.backup_duplicate_overwrite
- if self.prefs.indicate_download_error:
- severity = config.SERIOUS_ERROR
- problem = _("Backup image already exists")
- details = _("Source: %(source)s\nDestination: %(destination)s") \
- % {'source': image, 'destination': newBackupFile}
- if copyBackup :
- resolution = IMAGE_OVERWRITTEN
- else:
- resolution = IMAGE_SKIPPED
- logError(severity, problem, details, resolution)
-
- if copyBackup:
- if imageDownloaded:
- fileToCopy = newFile
- else:
- fileToCopy = image
- if os.path.isdir(backupPath):
- pathExists = True
+ for rootBackupDir in self.parentApp.backupVolumes:
+ if self.prefs.backup_device_autodetection:
+ if self.isImage:
+ backupDir = os.path.join(rootBackupDir, self.prefs.backup_identifier)
else:
- # recreate folder structure in backup location
- # cannot do os.makedirs(backupPath) - it can give bad results when using external drives
- # we know backupDir exists
- # all the components of subfolder may not
- folders = subfolder.split(os.path.sep)
- folderToMake = backupDir
- for f in folders:
- if f:
- folderToMake = os.path.join(folderToMake, f)
- if not os.path.isdir(folderToMake):
- try:
- os.mkdir(folderToMake)
- pathExists = True
- except (IOError, OSError), (errno, strerror):
- logError(config.SERIOUS_ERROR, _('Backing up error'),
- _("Destination directory could not be created: %(directory)s\n") %
- {'directory': folderToMake, } +
- _("Source: %(source)s\nDestination: %(destination)s\n") %
- {'source': image, 'destination': newBackupFile} +
- _("Error: %(errno)s %(strerror)s") % {'errno': errno, 'strerror': strerror},
- _('The image was not copied.')
- )
- pathExists = False
-
- if pathExists:
- shutil.copy2(fileToCopy, newBackupFile)
- backed_up = True
+ backupDir = os.path.join(rootBackupDir, self.prefs.video_backup_identifier)
+ else:
+ # photos and videos will be backed up into the same root folder, which the user has manually specified
+ backupDir = rootBackupDir
+ # if user has chosen auto detection, then:
+ # photos should only be backed up to photo backup locations
+ # videos should only be backed up to video backup locations
+ # if user did not choose autodetection, and the backup path doesn't exist, then
+ # will try to create it
+ if os.path.exists(backupDir) or not self.prefs.backup_device_autodetection:
+
+ backupPath = os.path.join(backupDir, subfolder)
+ newBackupFile = os.path.join(backupPath, newName)
+ copyBackup = True
+ if os.path.exists(newBackupFile):
+ # this check is of course not thread safe -- it doesn't need to be, because at this stage the file names are going to be unique
+ # (the folder structure is the same as the actual download folders, and the file names are unique in them)
+ copyBackup = self.prefs.backup_duplicate_overwrite
+ if self.prefs.indicate_download_error:
+ severity = config.SERIOUS_ERROR
+ problem = _("Backup of %(file_type)s already exists") % {'file_type': fileBeingDownloadedDisplay}
+ details = _("Source: %(source)s\nDestination: %(destination)s") \
+ % {'source': originalFile, 'destination': newBackupFile}
+ if copyBackup :
+ resolution = _("Backup %(file_type)s overwritten") % {'file_type': fileBeingDownloadedDisplay}
+ else:
+ fileNotBackedUpMessageDisplayed = True
+ if self.prefs.backup_device_autodetection:
+ volume = self.parentApp.backupVolumes[rootBackupDir].get_name()
+ resolution = _("%(file_type)s not backed up to %(volume)s") % {'file_type': fileBeingDownloadedDisplayCap, 'volume': volume}
+ else:
+ resolution = _("%(file_type)s not backed up") % {'file_type': fileBeingDownloadedDisplayCap}
+ logError(severity, problem, details, resolution)
+
+ if copyBackup:
+ if fileDownloaded:
+ fileToCopy = newFile
+ else:
+ fileToCopy = originalFile
+ if os.path.isdir(backupPath):
+ pathExists = True
+ else:
+ # recreate folder structure in backup location
+ # cannot do os.makedirs(backupPath) - it can give bad results when using external drives
+ # we know backupDir exists
+ # all the components of subfolder may not
+ folders = subfolder.split(os.path.sep)
+ folderToMake = backupDir
+ for f in folders:
+ if f:
+ folderToMake = os.path.join(folderToMake, f)
+ if not os.path.isdir(folderToMake):
+ try:
+ os.mkdir(folderToMake)
+ pathExists = True
+ except (IOError, OSError), (errno, strerror):
+ fileNotBackedUpMessageDisplayed = True
+ logError(config.SERIOUS_ERROR, _('Backing up error'),
+ _("Destination directory could not be created: %(directory)s\n") %
+ {'directory': folderToMake, } +
+ _("Source: %(source)s\nDestination: %(destination)s\n") %
+ {'source': originalFile, 'destination': newBackupFile} +
+ _("Error: %(errno)s %(strerror)s") % {'errno': errno, 'strerror': strerror},
+ _('The %(file_type)s was not backed up.') % {'file_type': fileBeingDownloadedDisplay}
+ )
+ pathExists = False
+ break
+
+ if pathExists:
+ shutil.copy2(fileToCopy, newBackupFile)
+ backed_up = True
except (IOError, OSError), (errno, strerror):
+ fileNotBackedUpMessageDisplayed = True
logError(config.SERIOUS_ERROR, _('Backing up error'),
_("Source: %(source)s\nDestination: %(destination)s\nError: %(errno)s %(strerror)s")
- % {'source': image, 'destination': newBackupFile, 'errno': errno, 'strerror': strerror},
- _('The image was not copied.'))
-
+ % {'source': originalFile, 'destination': newBackupFile, 'errno': errno, 'strerror': strerror},
+ _('The %(file_type)s was not backed up.') % {'file_type': fileBeingDownloadedDisplay}
+ )
+
+ if not backed_up and not fileNotBackedUpMessageDisplayed:
+ # The file has not been backed up to any medium
+ severity = config.SERIOUS_ERROR
+ problem = _("%(file_type)s could not be backed up") % {'file_type': fileBeingDownloadedDisplayCap}
+ details = _("Source: %(source)s") % {'source': originalFile}
+ if self.prefs.backup_device_autodetection:
+ resolution = _("No suitable backup volume was found")
+ else:
+ resolution = _("A backup location was not found")
+ logError(severity, problem, details, resolution)
+
return backed_up
def notifyAndUnmount():
@@ -1845,9 +2289,12 @@ class CopyPhotos(Thread):
else:
unmountMessage = ""
- message = _("%s images downloaded") % noImagesDownloaded
- if noImagesSkipped:
- message += "\n" + _("%s images skipped") % noImagesSkipped
+ file_types = file_types_by_number(noImagesDownloaded, noVideosDownloaded)
+ file_types_skipped = file_types_by_number(noImagesSkipped, noVideosSkipped)
+ message = _("%(noFiles)s %(filetypes)s downloaded") % {'noFiles':noFilesDownloaded, 'filetypes': file_types}
+ noFilesSkipped = noImagesSkipped + noVideosSkipped
+ if noFilesSkipped:
+ message += "\n" + _("%(noFiles)s %(filetypes)s skipped") % {'noFiles':noFilesSkipped, 'filetypes':file_types_skipped}
if unmountMessage:
message = "%s\n%s" % (message, unmountMessage)
@@ -1870,26 +2317,70 @@ class CopyPhotos(Thread):
+ def getThumbnail(fileMetadata):
+ thumbnail = orientation = None
+ if self.isImage:
+ try:
+ thumbnail = fileMetadata.getThumbnailData(MAX_THUMBNAIL_SIZE)
+ if not isinstance(thumbnail, types.StringType):
+ thumbnail = None
+ except:
+ thumbnail = None
+
+ if thumbnail is None:
+ logError(config.WARNING, _("Photo thumbnail could not be extracted"), fullFileName)
+ orientation = None
+ else:
+ orientation = fileMetadata.orientation(missing=None)
+ else:
+ # get thumbnail of video
+ # it may need to be generated
+ thumbnail = fileMetadata.getThumbnailData(MAX_THUMBNAIL_SIZE, tempWorkingDir)
+ if thumbnail:
+ orientation = 1
+ return thumbnail, orientation
+
+ def createTempDir(baseDir):
+ """
+ Create a temporary directory in which to download the photos to.
+
+ Returns the directory if it was created, else returns None.
+
+ Don't want to put it in system temp folder, as that is likely
+ to be on another partition and hence copying files from it
+ to the actual download folder will be slow!"""
+ try:
+ t = tempfile.mkdtemp(prefix='rapid-tmp-',
+ dir=baseDir)
+ return t
+ except OSError, (errno, strerror):
+ if not self.cardMedia.volume:
+ image_device = _("Source: %s\n") % self.cardMedia.getPath()
+ else:
+ _("Device: %s\n") % self.cardMedia.volume.get_name()
+ destination = _("Destination: %s") % baseDir
+ logError(config.CRITICAL_ERROR, _('Could not create temporary download directory'),
+ image_device + destination,
+ _("Download cannot proceed"))
+ cmd_line(_("Error:") + " " + _('Could not create temporary download directory'))
+ cmd_line(image_device + destination)
+ cmd_line(_("Download cannot proceed"))
+ display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
+ display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
+ display_queue.close("rw")
+ self.running = False
+ self.lock.release()
+ return None
+
self.hasStarted = True
display_queue.open('w')
#Do not try to handle any preference errors here
getPrefs(False)
- #check for presence of backup meditum
- if self.prefs.backup_images:
- if self.prefs.backup_missing <> config.IGNORE:
- if not len(self.parentApp.backupVolumes):
- if self.prefs.backup_missing == config.REPORT_ERROR:
- e = config.SERIOUS_ERROR
- else:
- e = config.WARNING
- logError(e, _("Backup device missing"), _("No backup device was detected."))
-
-
if not scanMedia():
- cmd_line(_("This device has no images to download from."))
- display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
+ cmd_line(_("This device has no %(types_searched_for)s to download from.") % {'types_searched_for': self.types_searched_for})
+ display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
display_queue.close("rw")
self.running = False
return
@@ -1925,17 +2416,47 @@ class CopyPhotos(Thread):
self.downloadStarted = True
cmd_line(_("Download has started from %s") % self.cardMedia.prettyName(limit=0))
+ #check for presence of backup path or volumes
+ if self.prefs.backup_images:
+ can_backup = True
+ if self.prefs.backup_missing == config.REPORT_ERROR:
+ e = config.SERIOUS_ERROR
+ elif self.prefs.backup_missing == config.REPORT_WARNING:
+ e = config.WARNING
+ if not self.prefs.backup_device_autodetection:
+ if not os.path.isdir(self.prefs.backup_location):
+ # the user has manually specified a path, but it
+ # does not exist. This is a problem.
+ try:
+ os.makedirs(self.prefs.backup_location)
+ except:
+ if self.prefs.backup_missing <> config.IGNORE:
+ logError(e, _("Backup path does not exist"),
+ _("The path %s could not be created") % path,
+ _("No backups can occur")
+ )
+ can_backup = False
+
+ elif self.prefs.backup_missing <> config.IGNORE:
+ if not len(self.parentApp.backupVolumes):
+ logError(e, _("Backup device missing"),
+ _("No backup device was automatically detected"),
+ _("No backups can occur"))
+ can_backup = False
+
if need_job_code and job_code == None:
sys.stderr.write(str(self.thread_id ) + ": job code should never be None\n")
self.imageRenamePrefsFactory.setJobCode('unknown-job-code')
self.subfolderPrefsFactory.setJobCode('unknown-job-code')
else:
self.imageRenamePrefsFactory.setJobCode(job_code)
+ self.videoRenamePrefsFactory.setJobCode(job_code)
self.subfolderPrefsFactory.setJobCode(job_code)
+ self.videoSubfolderPrefsFactory.setJobCode(job_code)
- # Some images may not have metadata (this
- # is unlikely for images straight out of a
- # camera, but it is possible for images that have been edited). If
+ # Some photos may not have metadata (this
+ # is unlikely for photos straight out of a
+ # camera, but it is possible for photos that have been edited). If
# only non-dynamic components make up the rest of an image name
# (e.g. text specified by the user), then relying on metadata will
# likely produce duplicate names.
@@ -1943,65 +2464,56 @@ class CopyPhotos(Thread):
needMetaDataToCreateUniqueImageName = self.imageRenamePrefsFactory.needImageMetaDataToCreateUniqueName()
# subfolder generation also need to be examined, but here the need is
- # not so exacting, since subfolders contain images, and naturally the
+ # not so exacting, since subfolders contain photos, and naturally the
# requirement to be unique is far more relaxed. However if subfolder
# generation relies entirely on metadata, that is a problem worth
# looking for
needMetaDataToCreateUniqueSubfolderName = self.subfolderPrefsFactory.needMetaDataToCreateUniqueName()
i = 0
- sizeDownloaded = noImagesDownloaded = noImagesSkipped = 0
- imagesDownloadedSuccessfully = []
+ sizeDownloaded = noFilesDownloaded = noImagesDownloaded = noVideosDownloaded = noImagesSkipped = noVideosSkipped = 0
+ filesDownloadedSuccessfully = []
- sizeImages = self.cardMedia.sizeOfImages(humanReadable = False)
- display_queue.put((self.parentApp.addToTotalDownloadSize, (sizeImages, )))
+ sizeFiles = self.cardMedia.sizeOfImagesAndVideos(humanReadable = False)
+ display_queue.put((self.parentApp.addToTotalDownloadSize, (sizeFiles, )))
display_queue.put((self.parentApp.setOverallDownloadMark, ()))
display_queue.put((self.parentApp.postStartDownloadTasks, ()))
- sizeImages = float(sizeImages)
- noImages = self.cardMedia.numberOfImages()
+ sizeFiles = float(sizeFiles)
+ noFiles = self.cardMedia.numberOfImagesAndVideos()
- baseDownloadDir = self.prefs.download_folder
- #create a temporary directory in which to download the photos to
- #don't want to put it in system temp folder, as that is likely
- #to be on another partition and hence copying files from it
- #to the download folder will be slow!
- try:
- tempWorkingDir = tempfile.mkdtemp(prefix='rapid-tmp-',
- dir=baseDownloadDir)
- except OSError, (errno, strerror):
- if not self.cardMedia.volume:
- image_device = _("Source: %s\n") % self.cardMedia.getPath()
- else:
- _("Image device: %s\n") % self.cardMedia.volume.get_name()
- destination = _("Destination: %s") % baseDownloadDir
- logError(config.CRITICAL_ERROR, _('Could not create temporary download directory'),
- image_device + destination,
- _("Download cannot proceed"))
- cmd_line(_("Error:") + " " + _('Could not create temporary download directory'))
- cmd_line(image_device + destination)
- cmd_line(_("Download cannot proceed"))
- display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
- display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
- display_queue.close("rw")
- self.running = False
- self.lock.release()
- return
-
-
- IMAGE_SKIPPED = _("Image skipped")
- IMAGE_OVERWRITTEN = _("Image overwritten")
- IMAGE_ALREADY_EXISTS = _("Image already exists")
+ if self.noImages > 0:
+ photoBaseDownloadDir = self.prefs.download_folder
+ if not checkDownloadPath(photoBaseDownloadDir):
+ return
+ photoTempWorkingDir = createTempDir(photoBaseDownloadDir)
+ if not photoTempWorkingDir:
+ return
+ else:
+ photoBaseDownloadDir = photoTempWorkingDir = None
+ if DOWNLOAD_VIDEO and self.noVideos > 0:
+ videoBaseDownloadDir = self.prefs.video_download_folder
+ if not checkDownloadPath(videoBaseDownloadDir):
+ return
+ videoTempWorkingDir = createTempDir(videoBaseDownloadDir)
+ if not videoTempWorkingDir:
+ return
+ else:
+ videoBaseDownloadDir = videoTempWorkingDir = None
addUniqueIdentifier = self.prefs.download_conflict_resolution == config.ADD_UNIQUE_IDENTIFIER
- usesSequenceElements = self.imageRenamePrefsFactory.usesSequenceElements()
- usesStoredSequenceNo = self.imageRenamePrefsFactory.usesTheSequenceElement(rn.STORED_SEQ_NUMBER)
- sequences. setUseOfSequenceElements(
+ usesImageSequenceElements = self.imageRenamePrefsFactory.usesSequenceElements()
+ usesVideoSequenceElements = self.videoRenamePrefsFactory.usesSequenceElements()
+ usesSequenceElements = usesVideoSequenceElements or usesImageSequenceElements
+
+ usesStoredSequenceNo = (self.imageRenamePrefsFactory.usesTheSequenceElement(rn.STORED_SEQ_NUMBER) or
+ self.videoRenamePrefsFactory.usesTheSequenceElement(rn.STORED_SEQ_NUMBER))
+ sequences.setUseOfSequenceElements(
self.imageRenamePrefsFactory.usesTheSequenceElement(rn.SESSION_SEQ_NUMBER),
self.imageRenamePrefsFactory.usesTheSequenceElement(rn.SEQUENCE_LETTER))
- while i < noImages:
+ while i < noFiles:
if not self.running:
self.lock.acquire()
self.running = True
@@ -2013,71 +2525,98 @@ class CopyPhotos(Thread):
return
# get information about the image to deduce image name and path
- name, root, size, modificationTime = self.cardMedia.images[i]
- image = os.path.join(root, name)
+ name, root, size, modificationTime = self.cardMedia.imagesAndVideos[i]
+ fullFileName = os.path.join(root, name)
- skipImage, imageMetadata, newName, newFile, path, subfolder, sequence_to_use = generateSubfolderAndFileName(
- image, name, needMetaDataToCreateUniqueImageName,
- needMetaDataToCreateUniqueSubfolderName)
+ self.isImage = media.isImage(name)
+ if self.isImage:
+ fileBeingDownloadedDisplay = _('photo')
+ fileBeingDownloadedDisplayCap = _('Photo')
+ fileSkippedDisplay = _("Photo skipped")
+ fileAlreadyExistsDisplay = _("Photo already exists")
+ fallback_date = None
+ tempWorkingDir = photoTempWorkingDir
+ baseDownloadDir = photoBaseDownloadDir
+ else:
+ fileBeingDownloadedDisplay = _('video')
+ fileBeingDownloadedDisplayCap = _('Video')
+ fileSkippedDisplay = _("Video skipped")
+ fileAlreadyExistsDisplay = _("Video already exists")
+ fallback_date = modificationTime
+ tempWorkingDir = videoTempWorkingDir
+ baseDownloadDir = videoBaseDownloadDir
+
+ skipFile, fileMetadata, newName, newFile, path, subfolder, sequence_to_use = generateSubfolderAndFileName(
+ fullFileName, name, needMetaDataToCreateUniqueImageName,
+ needMetaDataToCreateUniqueSubfolderName, fallback_date)
- if skipImage:
- noImagesSkipped += 1
+ if skipFile:
+ if self.isImage:
+ noImagesSkipped += 1
+ else:
+ noVideosSkipped += 1
else:
- imageDownloaded, newName, newFile = downloadImage(path, newFile, newName, name, image,
- imageMetadata, subfolder, sequence_to_use)
+ fileDownloaded, newName, newFile = downloadFile(path, newFile, newName, name, fullFileName,
+ fileMetadata, subfolder, sequence_to_use, fallback_date)
if self.prefs.backup_images:
- backed_up = backupImage(subfolder, newName, imageDownloaded, newFile, image)
+ if can_backup:
+ backed_up = backupFile(subfolder, newName, fileDownloaded, newFile, fullFileName)
+ else:
+ backed_up = False
- if imageDownloaded:
- noImagesDownloaded += 1
+ if fileDownloaded:
+ noFilesDownloaded += 1
+ if self.isImage:
+ noImagesDownloaded += 1
+ else:
+ noVideosDownloaded += 1
if self.prefs.backup_images and backed_up:
- imagesDownloadedSuccessfully.append(image)
+ filesDownloadedSuccessfully.append(fullFileName)
elif not self.prefs.backup_images:
- imagesDownloadedSuccessfully.append(image)
+ filesDownloadedSuccessfully.append(fullFileName)
else:
- noImagesSkipped += 1
- try:
- thumbnailType, thumbnail = imageMetadata.getThumbnailData()
- except:
- logError(config.WARNING, _("Image has no thumbnail"), image)
- thumbnail = orientation = None
- else:
- orientation = imageMetadata.orientation(missing=None)
- display_queue.put((image_hbox.addImage, (self.thread_id, thumbnail, orientation, image, imageDownloaded)))
+ if self.isImage:
+ noImagesSkipped += 1
+ else:
+ noVideosSkipped += 1
+
+ thumbnail, orientation = getThumbnail(fileMetadata)
+
+ display_queue.put((thumbnail_hbox.addImage, (self.thread_id, thumbnail, orientation, fullFileName, fileDownloaded, self.isImage)))
sizeDownloaded += size
- percentComplete = (sizeDownloaded / sizeImages) * 100
- if sizeDownloaded == sizeImages:
+ percentComplete = (sizeDownloaded / sizeFiles) * 100
+ if sizeDownloaded == sizeFiles:
self.downloadComplete = True
- progressBarText = _("%(number)s of %(total)s images copied") % {'number': i + 1, 'total': noImages}
+ progressBarText = _("%(number)s of %(total)s %(filetypes)s") % {'number': i + 1, 'total': noFiles, 'filetypes':self.display_file_types}
display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, percentComplete, progressBarText, size)))
i += 1
with self.statsLock:
- self.downloadStats.adjust(sizeDownloaded, noImagesDownloaded, noImagesSkipped, self.noWarnings, self.noErrors)
+ self.downloadStats.adjust(sizeDownloaded, noImagesDownloaded, noVideosDownloaded, noImagesSkipped, noVideosSkipped, self.noWarnings, self.noErrors)
if self.prefs.auto_delete:
j = 0
- for image in imagesDownloadedSuccessfully:
+ for imageOrVideo in filesDownloadedSuccessfully:
try:
- os.unlink(image)
+ os.unlink(imageOrVideo)
j += 1
except OSError, (errno, strerror):
- logError(config.SERIOUS_ERROR, _("Could not delete image from image device"),
- _("Image: %(source)s\nError: %(errno)s %(strerror)s")
+ logError(config.SERIOUS_ERROR, _("Could not delete photo or video from device"),
+ _("Photo: %(source)s\nError: %(errno)s %(strerror)s")
% {'source': image, 'errno': errno, 'strerror': strerror})
except:
- logError(config.SERIOUS_ERROR, _("Could not delete image from image device"),
- _("Image: %(source)s"))
+ logError(config.SERIOUS_ERROR, _("Could not delete photo or video from device"),
+ _("Photo: %(source)s"))
- cmd_line(_("Deleted %i images from image device") % j)
+ cmd_line(_("Deleted %(number)i %(filetypes)s from device") % {'number':j, 'filetypes':self.display_file_types})
- # must manually delete these variables, or else the media cannot be unmounted (bug in pyexiv or exiv2)
- del self.subfolderPrefsFactory, self.imageRenamePrefsFactory
+ # must manually delete these variables, or else the media cannot be unmounted (bug in some versions of pyexiv2 / exiv2)
+ del self.subfolderPrefsFactory, self.imageRenamePrefsFactory
try:
- del imageMetadata
+ del fileMetadata
except:
pass
@@ -2091,7 +2630,7 @@ class CopyPhotos(Thread):
display_queue.close("rw")
self.running = False
- if noImages:
+ if noFiles:
self.lock.release()
def startStop(self):
@@ -2171,13 +2710,11 @@ class MediaTreeView(gtk.TreeView):
self.append_column(column2)
self.show_all()
- def addCard(self, thread_id, cardName, sizeImages, noImages, progress = 0.0,
+ def addCard(self, thread_id, cardName, sizeFiles, noFiles, progress = 0.0,
progressBarText = ''):
- if not progressBarText:
- progressBarText = _("0 of %s images copied") % (noImages)
# add the row, and get a temporary pointer to the row
- iter = self.liststore.append((cardName, sizeImages, noImages,
+ iter = self.liststore.append((cardName, sizeFiles, noFiles,
progress, progressBarText))
self._setThreadMap(thread_id, iter)
@@ -2191,11 +2728,11 @@ class MediaTreeView(gtk.TreeView):
self.parentApp.media_collection_scrolledwindow.set_size_request(-1, height)
- def updateCard(self, thread_id, sizeImages, noImages):
+ def updateCard(self, thread_id, sizeFiles, noFiles):
if thread_id in self.mapThreadToRow:
iter = self._getThreadMap(thread_id)
- self.liststore.set_value(iter, 1, sizeImages)
- self.liststore.set_value(iter, 2, noImages)
+ self.liststore.set_value(iter, 1, sizeFiles)
+ self.liststore.set_value(iter, 2, noFiles)
else:
sys.stderr.write("FIXME: this card is unknown")
@@ -2245,7 +2782,7 @@ class MediaTreeView(gtk.TreeView):
col = self.get_column(0)
return self.get_background_area(path, col)[3]
-class ImageHBox(gtk.HBox):
+class ThumbnailHBox(gtk.HBox):
"""
Displays thumbnails of the images being downloaded
"""
@@ -2256,49 +2793,63 @@ class ImageHBox(gtk.HBox):
self.padding = hd.CONTROL_IN_TABLE_SPACE / 2
#create image used to lighten thumbnails
- self.white = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width=100, height=100)
+ self.white = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width=MAX_THUMBNAIL_SIZE, height=MAX_THUMBNAIL_SIZE)
#fill with white
self.white.fill(0xffffffff)
#load missing image
- self.missingThumbnail = gtk.gdk.pixbuf_new_from_file_at_size(paths.share_dir('glade3/image-missing.svg'), 100, 100)
+ self.missingThumbnail = gtk.gdk.pixbuf_new_from_file_at_size(paths.share_dir('glade3/image-missing.svg'), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE)
+ self.videoThumbnail = gtk.gdk.pixbuf_new_from_file_at_size(paths.share_dir('glade3/video.svg'), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE)
- def addImage(self, thread_id, thumbnail, orientation, filename, imageDownloaded):
+ def addImage(self, thread_id, thumbnail, orientation, filename, fileDownloaded, isImage):
"""
Add thumbnail
Orientation indicates if the thumbnail needs to be rotated or not.
"""
- if not thumbnail:
- pixbuf = self.missingThumbnail
- else:
- try:
- pbloader = gdk.PixbufLoader()
- pbloader.write(thumbnail)
- # Get the resulting pixbuf and build an image to be displayed
- pixbuf = pbloader.get_pixbuf()
- pbloader.close()
-
- except:
- log_dialog.addMessage(thread_id, config.WARNING,
- _('Thumbnail cannot be displayed'), filename,
- _('It may be corrupted'))
+ if isImage:
+ if not thumbnail:
pixbuf = self.missingThumbnail
+ else:
+ try:
+ pbloader = gdk.PixbufLoader()
+ pbloader.write(thumbnail)
+ pbloader.close()
+ # Get the resulting pixbuf and build an image to be displayed
+ pixbuf = pbloader.get_pixbuf()
+ except:
+ log_dialog.addMessage(thread_id, config.WARNING,
+ _("Photo thumbnail could not be extracted"), filename,
+ _('It may be corrupted'))
+ pbloader = None
+ pixbuf = self.missingThumbnail
+ else:
+ # the file downloaded is a video, not a photo or image
+ # if thumbnail is passed in, it is already in pixbuf format
+ if thumbnail:
+ pixbuf = thumbnail
+ else:
+ pixbuf = self.videoThumbnail
if not pixbuf:
+ # get_pixbuf() can return None if not could not render the image
log_dialog.addMessage(thread_id, config.WARNING,
- _('Thumbnail cannot be displayed'), filename,
+ _("Photo thumbnail could not be extracted"), filename,
_('It may be corrupted'))
pixbuf = self.missingThumbnail
else:
# rotate if necessary
if orientation == 8:
pixbuf = pixbuf.rotate_simple(gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
+ elif orientation == 6:
+ pixbuf = pixbuf.rotate_simple(gdk.PIXBUF_ROTATE_CLOCKWISE)
+ elif orientation == 3:
+ pixbuf = pixbuf.rotate_simple(gdk.PIXBUF_ROTATE_UPSIDEDOWN)
# scale to size
- pixbuf = common.scale2pixbuf(100, 100, pixbuf)
- if not imageDownloaded:
+ pixbuf = common.scale2pixbuf(MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, pixbuf)
+ if not fileDownloaded:
# lighten it
self.white.composite(pixbuf, 0, 0, pixbuf.props.width, pixbuf.props.height, 0, 0, 1.0, 1.0, gtk.gdk.INTERP_HYPER, 180)
@@ -2324,7 +2875,7 @@ class UseDeviceDialog(gtk.Dialog):
self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader-about.png'))
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#usedeviceprompt
- prompt_label = gtk.Label(_('Should this device or partition be used to download images from?'))
+ prompt_label = gtk.Label(_('Should this device or partition be used to download photos or videos from?'))
prompt_label.set_line_wrap(True)
prompt_hbox = gtk.HBox()
prompt_hbox.pack_start(prompt_label, False, False, padding=6)
@@ -2541,7 +3092,8 @@ class LogDialog(gnomeglade.Component):
self.log_textview.set_cursor_visible(False)
self.textbuffer = self.log_textview.get_buffer()
- self.problemTag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD)
+ self.errorTag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD, foreground="red")
+ self.warningTag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD)
self.resolutionTag = self.textbuffer.create_tag(style=pango.STYLE_ITALIC)
def addMessage(self, thread_id, severity, problem, details, resolution):
@@ -2552,7 +3104,10 @@ class LogDialog(gnomeglade.Component):
self.parentApp.warning_vseparator.show()
iter = self.textbuffer.get_end_iter()
- self.textbuffer.insert_with_tags(iter, problem +"\n", self.problemTag)
+ if severity in [config.CRITICAL_ERROR, config.SERIOUS_ERROR]:
+ self.textbuffer.insert_with_tags(iter, problem +"\n", self.errorTag)
+ else:
+ self.textbuffer.insert_with_tags(iter, problem +"\n", self.warningTag)
if details:
iter = self.textbuffer.get_end_iter()
self.textbuffer.insert(iter, details + "\n")
@@ -2609,6 +3164,8 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
# sys.exit(0)
self.widget.show()
+
+ self.checkIfFirstTimeProgramEverRun()
displayPreferences = self.checkForUpgrade(__version__)
self.prefs.program_version = __version__
@@ -2626,7 +3183,7 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
displayPreferences = not self.checkPreferencesOnStartup()
# display download information using threads
- global media_collection_treeview, image_hbox, log_dialog
+ global media_collection_treeview, thumbnail_hbox, log_dialog
global download_queue, image_queue, log_queue
global workers
@@ -2683,8 +3240,8 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
self.media_collection_vbox.pack_start(media_collection_treeview)
#thumbnail display
- image_hbox = ImageHBox(self)
- self.image_viewport.add(image_hbox)
+ thumbnail_hbox = ThumbnailHBox(self)
+ self.image_viewport.add(thumbnail_hbox)
self.image_viewport.modify_bg(gtk.STATE_NORMAL, gdk.color_parse("white"))
self.set_display_thumbnails(self.prefs.display_thumbnails)
@@ -2748,23 +3305,33 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
self.rapidapp.present()
else:
self.running = True
+# if not using_gio:
self.main()
+# else:
+# mainloop = gobject.MainLoop()
+# mainloop.run()
self.running = False
def setTestingEnv(self):
- self.prefs.program_version = '0.0.8~b7'
- r = ['Date time', 'Image date', 'YYYYMMDD', 'Text', '-', '', 'Date time', 'Image date', 'HHMM', 'Text', '-', '', 'Session number', '1', 'Three digits', 'Text', '-iso', '', 'Metadata', 'ISO', '', 'Text', '-f', '', 'Metadata', 'Aperture', '', 'Text', '-', '', 'Metadata', 'Focal length', '', 'Text', 'mm-', '', 'Metadata', 'Exposure time', '', 'Filename', 'Extension', 'lowercase']
- self.prefs.image_rename = r
+ #self.prefs.program_version = '0.0.8~b7'
+ p = ['Date time', 'Image date', 'YYYYMMDD', 'Text', '-', '', 'Date time', 'Image date', 'HHMM', 'Text', '-', '', rn.SEQUENCES, rn.DOWNLOAD_SEQ_NUMBER, rn.SEQUENCE_NUMBER_3, 'Text', '-iso', '', 'Metadata', 'ISO', '', 'Text', '-f', '', 'Metadata', 'Aperture', '', 'Text', '-', '', 'Metadata', 'Focal length', '', 'Text', 'mm-', '', 'Metadata', 'Exposure time', '', 'Filename', 'Extension', 'lowercase']
+ v = ['Date time', 'Video date', 'YYYYMMDD', 'Text', '-', '', 'Date time', 'Video date', 'HHMM', 'Text', '-', '', 'Sequences', 'Downloads today', 'One digit', 'Text', '-', '', 'Metadata', 'Width', '', 'Text', 'x', '', 'Metadata', 'Height', '', 'Filename', 'Extension', 'lowercase']
+ f = '/home/damon/store/rapid-dump'
+ self.prefs.image_rename = p
+ self.prefs.video_rename = v
+ self.prefs.download_folder = f
+ self.prefs.video_download_folder = f
+
def checkImageDevicePathOnStartup(self):
msg = None
if not os.path.isdir(self.prefs.device_location):
- msg = _("Sorry, this image location does not exist:\n%(path)s\n\nPlease resolve the problem, or modify your preferences." % {"path": self.prefs.device_location})
+ msg = _("Sorry, this device location does not exist:\n%(path)s\n\nPlease resolve the problem, or modify your preferences." % {"path": self.prefs.device_location})
if msg:
sys.stderr.write(msg +'\n')
- misc.run_dialog(_("Problem with Image Location Folder"), msg,
+ misc.run_dialog(_("Problem with Device Location Folder"), msg,
self,
gtk.MESSAGE_ERROR)
return False
@@ -2772,22 +3339,36 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
return True
def checkDownloadPathOnStartup(self):
- msg = None
- if not os.path.isdir(self.prefs.download_folder):
- msg = _("Sorry, the Download Folder does not exist. Please create the folder, or modify your preferences")
+ if DOWNLOAD_VIDEO:
+ paths = ((self.prefs.download_folder, _('Photo')), (self.prefs.video_download_folder, _('Video')))
else:
- #unfortunately 'os.access(self.prefs.download_folder, os.W_OK)' is not reliable
- try:
- tempWorkingDir = tempfile.mkdtemp(prefix='rapid-tmp-',
- dir=self.prefs.download_folder)
- except:
- msg = _("Sorry, the Download Folder exists but cannot be written to. Please check the folder's permissions, or modify your preferences")
+ paths = ((self.prefs.download_folder, _('Photo')),)
+ msg = ''
+ noProblems = 0
+ for path, file_type in paths:
+ if not os.path.isdir(path):
+ msg += _("The %(file_type)s Download Folder does not exist.\n") % {'file_type': file_type}
+ noProblems += 1
else:
- os.rmdir(tempWorkingDir)
+ #unfortunately 'os.access(self.prefs.download_folder, os.W_OK)' is not reliable
+ try:
+ tempWorkingDir = tempfile.mkdtemp(prefix='rapid-tmp-',
+ dir=path)
+ except:
+ noProblems += 1
+ msg += _("The %(file_type)s Download Folder exists but cannot be written to.\n") % {'file_type': file_type}
+ else:
+ os.rmdir(tempWorkingDir)
if msg:
- sys.stderr.write(msg +'\n')
- misc.run_dialog(_("Problem with Download Folder"), msg,
+ msg = _("Sorry, problems were encountered with your download folders. Please fix the problems or modify the preferences.\n\n") + msg
+ sys.stderr.write(msg)
+ if noProblems == 1:
+ title = _("Problem with Download Folder")
+ else:
+ title = _("Problem with Download Folders")
+
+ misc.run_dialog(title, msg,
self,
gtk.MESSAGE_ERROR)
return False
@@ -2795,7 +3376,7 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
return True
def checkPreferencesOnStartup(self):
- prefsOk = rn.checkPreferencesForValidity(self.prefs.image_rename, self.prefs.subfolder)
+ prefsOk = rn.checkPreferencesForValidity(self.prefs.image_rename, self.prefs.subfolder, self.prefs.video_rename, self.prefs.video_subfolder)
if not prefsOk:
msg = _("There is an error in the program preferences.")
msg += " " + _("Some preferences will be reset.")
@@ -2890,7 +3471,31 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
# FIXME: what happens to these workers that are waiting? How will the user start their download?
# check if need to add code to start button
- def checkForUpgrade(self, runningVersion):
+ def checkIfFirstTimeProgramEverRun(self):
+ """
+ if this is the first time the program has been run, then
+ might need to create default directories
+ """
+ if len(self.prefs.program_version) == 0:
+ path = getDefaultPhotoLocation(ignore_missing_dir=True)
+ if not os.path.isdir(path):
+ cmd_line(_("Creating photo download folder %(folder)s") % {'folder':path})
+ try:
+ os.makedirs(path)
+ self.prefs.download_folder = path
+ except:
+ cmd_line(_("Failed to create default photo download folder %(folder)s") % {'folder':path})
+ if DOWNLOAD_VIDEO:
+ path = getDefaultVideoLocation(ignore_missing_dir=True)
+ if not os.path.isdir(path):
+ cmd_line(_("Creating video download folder %(folder)s") % {'folder':path})
+ try:
+ os.makedirs(path)
+ self.prefs.video_download_folder = path
+ except:
+ cmd_line(_("Failed to create default video download folder %(folder)s") % {'folder':path})
+
+ def checkForUpgrade(self, runningVersion):
""" Checks if the running version of the program is different from the version recorded in the preferences.
If the version is different, then the preferences are checked to see whether they should be upgraded or not.
@@ -2900,7 +3505,7 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
displayPrefs = upgraded = False
previousVersion = self.prefs.program_version
- if previousVersion:
+ if len(previousVersion) > 0:
# the program has been run previously for this user
pv = common.pythonifyVersion(previousVersion)
@@ -2911,7 +3516,7 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
if pv != rv:
if pv > rv:
- prefsOk = rn.checkPreferencesForValidity(self.prefs.image_rename, self.prefs.subfolder)
+ prefsOk = rn.checkPreferencesForValidity(self.prefs.image_rename, self.prefs.subfolder, self.prefs.video_rename, self.prefs.video_subfolder)
msg = _("A newer version of this program was previously run on this computer.\n\n")
if prefsOk:
@@ -2925,8 +3530,8 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
else:
cmd_line(_("This version of the program is newer than the previously run version. Checking preferences."))
- if rn.checkPreferencesForValidity(self.prefs.image_rename, self.prefs.subfolder, previousVersion):
- upgraded, imageRename, subfolder = rn.upgradePreferencesToCurrent(self.prefs.image_rename, self.prefs.subfolder, previousVersion)
+ if rn.checkPreferencesForValidity(self.prefs.image_rename, self.prefs.subfolder, self.prefs.video_rename, self.prefs.video_subfolder, previousVersion):
+ upgraded, imageRename, subfolder = rn.upgradePreferencesToCurrent(self.prefs.image_rename, self.prefs.subfolder, previousVersion)
if upgraded:
self.prefs.image_rename = imageRename
self.prefs.subfolder = subfolder
@@ -2942,6 +3547,7 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
misc.run_dialog(title, msg)
displayPrefs = True
+
return displayPrefs
def initPyNotify(self):
@@ -2974,10 +3580,13 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
cmd_line(_("Warning: desktop environment notification server is incorrectly configured."))
self.notification_icon_size = 48
else:
- if info['name'] == 'Notification Daemon':
+ try:
+ if info['name'] == 'Notification Daemon':
+ self.notification_icon_size = 128
+ else:
+ self.notification_icon_size = 48
+ except:
self.notification_icon_size = 48
- else:
- self.notification_icon_size = 128
self.application_icon = gtk.gdk.pixbuf_new_from_file_at_size(
paths.share_dir('glade3/rapid-photo-downloader-about.png'),
@@ -3071,12 +3680,11 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
isBackupVolume = self.checkIfBackupVolume(path)
if isBackupVolume:
- backupPath = os.path.join(path, self.prefs.backup_identifier)
if path not in self.backupVolumes:
- self.backupVolumes[backupPath] = volume
+ self.backupVolumes[path] = volume
self.rapid_statusbar.push(self.statusbar_context_id, self.displayBackupVolumes())
- elif media.isImageMedia(path) or self.searchForPsd():
+ elif media.is_DCIM_Media(path) or self.searchForPsd():
if self.searchForPsd() and path not in self.prefs.device_whitelist:
# prompt user if device should be used or not
self.getUseDevice(path, volume, self.prefs.auto_download_upon_device_insertion)
@@ -3132,9 +3740,8 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
# fourth scenario - nothing to do
# remove backup volumes
- backupPath = os.path.join(path, self.prefs.backup_identifier)
- if backupPath in self.backupVolumes:
- del self.backupVolumes[backupPath]
+ if path in self.backupVolumes:
+ del self.backupVolumes[path]
self.rapid_statusbar.push(self.statusbar_context_id, self.displayBackupVolumes())
# may need to disable download button
@@ -3167,9 +3774,12 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
Checks against user preferences.
"""
+ identifiers = [self.prefs.backup_identifier]
+ if DOWNLOAD_VIDEO:
+ identifiers.append(self.prefs.video_backup_identifier)
if self.prefs.backup_images:
if self.prefs.backup_device_autodetection:
- if media.isBackupMedia(path, self.prefs.backup_identifier):
+ if media.isBackupMedia(path, identifiers):
return True
elif path == self.prefs.backup_location:
# user manually specified the path
@@ -3226,14 +3836,14 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
self._printDetectedDevice(volume.get_name(limit=0), path)
isBackupVolume = self.checkIfBackupVolume(path)
if isBackupVolume:
- backupPath = os.path.join(path, self.prefs.backup_identifier)
- self.backupVolumes[backupPath] = volume
- elif self.prefs.device_autodetection and (media.isImageMedia(path) or self.searchForPsd()):
+ #backupPath = os.path.join(path, self.prefs.backup_identifier)
+ self.backupVolumes[path] = volume
+ elif self.prefs.device_autodetection and (media.is_DCIM_Media(path) or self.searchForPsd()):
volumeList.append((path, volume))
if not self.prefs.device_autodetection:
- # user manually specified the path from which to download images
+ # user manually specified the path from which to download
path = self.prefs.device_location
if path:
cmd_line(_("Using manually specified path") + " %s" % path)
@@ -3242,8 +3852,9 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
if self.prefs.backup_images:
if not self.prefs.backup_device_autodetection:
# user manually specified backup location
+ # will backup to this path, but don't need any volume info associated with it
self.backupVolumes[self.prefs.backup_location] = None
- self.rapid_statusbar.push(self.statusbar_context_id, '')
+ self.rapid_statusbar.push(self.statusbar_context_id, _('Backing up to %(path)s') % {'path':self.prefs.backup_location})
else:
self.rapid_statusbar.push(self.statusbar_context_id, self.displayBackupVolumes())
@@ -3404,9 +4015,14 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object):
if self.downloadComplete():
if self.displayDownloadSummaryNotification:
message = _("All downloads complete")
- message += "\n%s " % self.downloadStats.noImagesDownloaded + _("images downloaded")
+ if self.downloadStats.noImagesDownloaded:
+ message += "\n%s " % self.downloadStats.noImagesDownloaded + _("photos downloaded")
if self.downloadStats.noImagesSkipped:
- message = "%s\n%s " % (message, self.downloadStats.noImagesSkipped) + _("images skipped")
+ message = "%s\n%s " % (message, self.downloadStats.noImagesSkipped) + _("photos skipped")
+ if self.downloadStats.noVideosDownloaded:
+ message += "\n%s " % self.downloadStats.noVideosDownloaded + _("videos downloaded")
+ if self.downloadStats.noVideosSkipped:
+ message = "%s\n%s " % (message, self.downloadStats.noVideosSkipped) + _("videos skipped")
if self.downloadStats.noWarnings:
message = "%s\n%s " % (message, self.downloadStats.noWarnings) + _("warnings")
if self.downloadStats.noErrors:
@@ -3629,6 +4245,7 @@ class Volume:
""" Transistion to gvfs from gnomevfs"""
def __init__(self, volume):
self.volume = volume
+ self.using_gio = using_gio
def get_name(self, limit=config.MAX_LENGTH_DEVICE_NAME):
if using_gio:
@@ -3686,15 +4303,17 @@ class DownloadStats:
def __init__(self):
self.clear()
- def adjust(self, size, noImagesDownloaded, noImagesSkipped, noWarnings, noErrors):
+ def adjust(self, size, noImagesDownloaded, noVideosDownloaded, noImagesSkipped, noVideosSkipped, noWarnings, noErrors):
self.downloadSize += size
self.noImagesDownloaded += noImagesDownloaded
+ self.noVideosDownloaded += noVideosDownloaded
self.noImagesSkipped += noImagesSkipped
+ self.noVideosSkipped += noVideosSkipped
self.noWarnings += noWarnings
self.noErrors += noErrors
def clear(self):
- self.noImagesDownloaded = self.noImagesSkipped = 0
+ self.noImagesDownloaded = self.noVideosDownloaded = self.noImagesSkipped = self.noVideosSkipped = 0
self.downloadSize = 0
self.noWarnings = self.noErrors = 0
@@ -3799,7 +4418,8 @@ def start ():
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help=_("display program information on the command line as the program runs (default: %default)"))
parser.add_option("-q", "--quiet", action="store_false", dest="verbose", help=_("only output errors to the command line"))
# image file extensions are recognized RAW files plus TIFF and JPG
- parser.add_option("-e", "--extensions", action="store_true", dest="extensions", help=_("list image file extensions the program recognizes and exit"))
+ parser.add_option("-e", "--extensions", action="store_true", dest="extensions", help=_("list photo and video file extensions the program recognizes and exit"))
+ parser.add_option("--reset-settings", action="store_true", dest="reset", help=_("reset all program settings and preferences and exit"))
(options, args) = parser.parse_args()
global verbose
verbose = options.verbose
@@ -3808,23 +4428,43 @@ def start ():
atexit.register(programStatus)
if options.extensions:
- exts = config.RAW_FILE_EXTENSIONS + config.NON_RAW_IMAGE_FILE_EXTENSIONS
- v = ''
- for e in exts[:-1]:
- v += '%s, ' % e.upper()
- v = v[:-1] + ' '+ (_('and %s') % exts[-1].upper())
- print v
+ extensions = ((metadata.RAW_FILE_EXTENSIONS + metadata.NON_RAW_IMAGE_FILE_EXTENSIONS, _("Photos:")), (videometadata.VIDEO_FILE_EXTENSIONS, _("Videos:")))
+ for exts, file_type in extensions:
+ v = ''
+ for e in exts[:-1]:
+ v += '%s, ' % e.upper()
+ v = file_type + " " + v[:-1] + ' '+ (_('and %s') % exts[-1].upper())
+ print v
+
+ sys.exit(0)
+
+ if options.reset:
+ prefs = RapidPreferences()
+ prefs.reset()
+ print _("All settings and preferences have been reset")
sys.exit(0)
+ cmd_line(_("Rapid Photo Downloader") + " %s" % config.version)
+ cmd_line(_("Using") + " pyexiv2 " + metadata.version_info())
+ cmd_line(_("Using") + " exiv2 " + metadata.exiv2_version_info())
+ if DOWNLOAD_VIDEO:
+ cmd_line(_("Using") + " kaa " + videometadata.version_info())
+ else:
+ cmd_line(_("\n" + "Video downloading functionality disabled.\nTo download videos, please install the kaa metadata package for python.") + "\n")
+
if using_gio:
cmd_line(_("Using") + " GIO")
+ gobject.threads_init()
else:
# Which volume management code is being used (GIO or GnomeVFS)
cmd_line(_("Using") + " GnomeVFS")
+ gdk.threads_init()
- gdk.threads_init()
+
+
display_queue.open("rw")
tube.tube_add_watch(display_queue, updateDisplay)
+
gdk.threads_enter()
# run only a single instance of the application
@@ -3839,6 +4479,7 @@ def start ():
app = dbus.Interface (object, config.DBUS_NAME)
app.start()
+
gdk.threads_leave()
if __name__ == "__main__":
diff --git a/rapid/renamesubfolderprefs.py b/rapid/renamesubfolderprefs.py
index a449fd7..feb7366 100644
--- a/rapid/renamesubfolderprefs.py
+++ b/rapid/renamesubfolderprefs.py
@@ -77,7 +77,7 @@ ORDER_KEY = "__order__"
# PLEASE NOTE: these values are duplicated in a dummy class whose function
# is to have them put into the translation template. If you change the values below
-# then change the value in class i18TranslateMeThanks as well!! Thanks!!
+# then you MUST change the value in class i18TranslateMeThanks as well!!
# *** Level 0
DATE_TIME = 'Date time'
@@ -95,12 +95,14 @@ SEPARATOR = os.sep
IMAGE_DATE = 'Image date'
TODAY = 'Today'
YESTERDAY = 'Yesterday'
+VIDEO_DATE = 'Video date'
# File name
NAME_EXTENSION = 'Name + extension'
NAME = 'Name'
EXTENSION = 'Extension'
IMAGE_NUMBER = 'Image number'
+VIDEO_NUMBER = 'Video number'
# Metadata
APERTURE = 'Aperture'
@@ -115,6 +117,13 @@ SERIAL_NUMBER = 'Serial number'
SHUTTER_COUNT = 'Shutter count'
OWNER_NAME = 'Owner name'
+# Video metadata
+CODEC = 'Codec'
+WIDTH = 'Width'
+HEIGHT = 'Height'
+FPS = 'Frames Per Second'
+LENGTH = 'Length'
+
#Image sequences
DOWNLOAD_SEQ_NUMBER = 'Downloads today'
SESSION_SEQ_NUMBER = 'Session number'
@@ -158,7 +167,8 @@ SEQUENCE_NUMBER_7 = "Seven digits"
SUBSECONDS = 'Subseconds'
-# ****** note if changing LIST_DATE_TIME_L2, update the default subfolder preference below :D *****
+# ****** NOTE 1: if changing LIST_DATE_TIME_L2, you MUST update the default subfolder preference below *****
+# ****** NOTE 2: if changing LIST_DATE_TIME_L2, you MUST update DATE_TIME_CONVERT below *****
LIST_DATE_TIME_L2 = ['YYYYMMDD', 'YYYY-MM-DD','YYMMDD', 'YY-MM-DD',
'MMDDYYYY', 'MMDDYY', 'MMDD',
'DDMMYYYY', 'DDMMYY', 'YYYY', 'YY',
@@ -168,7 +178,8 @@ LIST_DATE_TIME_L2 = ['YYYYMMDD', 'YYYY-MM-DD','YYMMDD', 'YY-MM-DD',
LIST_IMAGE_DATE_TIME_L2 = LIST_DATE_TIME_L2 + [SUBSECONDS]
-DEFAULT_SUBFOLDER_PREFS = [DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[9], '/', '', '', DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[0]]
+DEFAULT_SUBFOLDER_PREFS = [DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[9], '/', '', '', DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[0]]
+DEFAULT_VIDEO_SUBFOLDER_PREFS = [DATE_TIME, VIDEO_DATE, LIST_DATE_TIME_L2[9], '/', '', '', DATE_TIME, VIDEO_DATE, LIST_DATE_TIME_L2[0]]
class i18TranslateMeThanks:
""" this class is never used in actual running code
@@ -184,6 +195,7 @@ class i18TranslateMeThanks:
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#jobcode
_('Job code')
_('Image date')
+ _('Video date')
_('Today')
_('Yesterday')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename
@@ -194,6 +206,7 @@ class i18TranslateMeThanks:
_('Extension')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename
_('Image number')
+ _('Video number')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata
_('Aperture')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata
@@ -216,6 +229,11 @@ class i18TranslateMeThanks:
_('Shutter count')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata
_('Owner name')
+ _('Codec')
+ _('Width')
+ _('Height')
+ _('Length')
+ _('Frames Per Second')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequencenumbers
_('Downloads today')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequencenumbers
@@ -338,6 +356,7 @@ LIST_SHUTTER_COUNT_L2 = [
# Level 1
LIST_DATE_TIME_L1 = [IMAGE_DATE, TODAY, YESTERDAY]
+LIST_VIDEO_DATE_TIME_L1 = [VIDEO_DATE, TODAY, YESTERDAY]
DICT_DATE_TIME_L1 = {
IMAGE_DATE: LIST_IMAGE_DATE_TIME_L2,
@@ -345,6 +364,13 @@ DICT_DATE_TIME_L1 = {
YESTERDAY: LIST_DATE_TIME_L2,
ORDER_KEY: LIST_DATE_TIME_L1
}
+
+VIDEO_DICT_DATE_TIME_L1 = {
+ VIDEO_DATE: LIST_IMAGE_DATE_TIME_L2,
+ TODAY: LIST_DATE_TIME_L2,
+ YESTERDAY: LIST_DATE_TIME_L2,
+ ORDER_KEY: LIST_VIDEO_DATE_TIME_L1
+ }
LIST_FILENAME_L1 = [NAME_EXTENSION, NAME, EXTENSION, IMAGE_NUMBER]
@@ -357,6 +383,15 @@ DICT_FILENAME_L1 = {
ORDER_KEY: LIST_FILENAME_L1
}
+LIST_VIDEO_FILENAME_L1 = [NAME_EXTENSION, NAME, EXTENSION, VIDEO_NUMBER]
+
+DICT_VIDEO_FILENAME_L1 = {
+ NAME_EXTENSION: LIST_CASE_L2,
+ NAME: LIST_CASE_L2,
+ EXTENSION: LIST_CASE_L2,
+ VIDEO_NUMBER: LIST_IMAGE_NUMBER_L2,
+ ORDER_KEY: LIST_VIDEO_FILENAME_L1
+ }
LIST_SUBFOLDER_FILENAME_L1 = [EXTENSION]
@@ -372,7 +407,9 @@ LIST_METADATA_L1 = [APERTURE, ISO, EXPOSURE_TIME, FOCAL_LENGTH,
SHORT_CAMERA_MODEL_HYPHEN,
SERIAL_NUMBER,
SHUTTER_COUNT,
- OWNER_NAME]
+ OWNER_NAME]
+
+LIST_VIDEO_METADATA_L1 = [CODEC, WIDTH, HEIGHT, LENGTH, FPS]
DICT_METADATA_L1 = {
APERTURE: None,
@@ -388,7 +425,15 @@ DICT_METADATA_L1 = {
OWNER_NAME: LIST_CASE_L2,
ORDER_KEY: LIST_METADATA_L1
}
-
+
+DICT_VIDEO_METADATA_L1 = {
+ CODEC: LIST_CASE_L2,
+ WIDTH: None,
+ HEIGHT: None,
+ LENGTH: None,
+ FPS: None,
+ ORDER_KEY: LIST_VIDEO_METADATA_L1
+ }
LIST_SEQUENCE_L1 = [
DOWNLOAD_SEQ_NUMBER,
@@ -412,6 +457,8 @@ DICT_SEQUENCE_L1 = {
LIST_IMAGE_RENAME_L0 = [DATE_TIME, TEXT, FILENAME, METADATA,
SEQUENCES, JOB_CODE]
+LIST_VIDEO_RENAME_L0 = LIST_IMAGE_RENAME_L0
+
DICT_IMAGE_RENAME_L0 = {
DATE_TIME: DICT_DATE_TIME_L1,
@@ -422,6 +469,16 @@ DICT_IMAGE_RENAME_L0 = {
JOB_CODE: None,
ORDER_KEY: LIST_IMAGE_RENAME_L0
}
+
+DICT_VIDEO_RENAME_L0 = {
+ DATE_TIME: VIDEO_DICT_DATE_TIME_L1,
+ TEXT: None,
+ FILENAME: DICT_VIDEO_FILENAME_L1,
+ METADATA: DICT_VIDEO_METADATA_L1,
+ SEQUENCES: DICT_SEQUENCE_L1,
+ JOB_CODE: None,
+ ORDER_KEY: LIST_VIDEO_RENAME_L0
+ }
LIST_SUBFOLDER_L0 = [DATE_TIME, TEXT, FILENAME, METADATA, JOB_CODE, SEPARATOR]
@@ -434,6 +491,18 @@ DICT_SUBFOLDER_L0 = {
SEPARATOR: None,
ORDER_KEY: LIST_SUBFOLDER_L0
}
+
+LIST_VIDEO_SUBFOLDER_L0 = [DATE_TIME, TEXT, FILENAME, METADATA, JOB_CODE, SEPARATOR]
+
+DICT_VIDEO_SUBFOLDER_L0 = {
+ DATE_TIME: VIDEO_DICT_DATE_TIME_L1,
+ TEXT: None,
+ FILENAME: DICT_SUBFOLDER_FILENAME_L1,
+ METADATA: DICT_VIDEO_METADATA_L1,
+ JOB_CODE: None,
+ SEPARATOR: None,
+ ORDER_KEY: LIST_VIDEO_SUBFOLDER_L0
+ }
# preference elements that require metadata
# note there is no need to specify lower level elements if a higher level
@@ -568,7 +637,7 @@ def _upgradePreferencesToCurrent(prefs, previousVersion):
return (upgraded, p)
-def upgradePreferencesToCurrent(imageRenamePrefs, subfolderPrefs, previousVersion):
+def upgradePreferencesToCurrent(imageRenamePrefs, subfolderPrefs, previousVersion):
"""Upgrades user preferences to current version
returns True if the preferences were upgraded"""
@@ -585,13 +654,22 @@ def usesJobCode(prefs):
return True
return False
-def checkPreferencesForValidity(imageRenamePrefs, subfolderPrefs, version=config.version):
- """Returns true if the passed in preferences are valid"""
+def checkPreferencesForValidity(imageRenamePrefs, subfolderPrefs, videoRenamePrefs, videoSubfolderPrefs, version=config.version):
+ """
+ Checks preferences for validity (called at program startup)
+
+ Returns true if the passed in preferences are valid, else returns False
+ """
if version == config.version:
try:
- checkPreferenceValid(DICT_SUBFOLDER_L0, subfolderPrefs)
- checkPreferenceValid(DICT_IMAGE_RENAME_L0, imageRenamePrefs)
+ tests = ((imageRenamePrefs, ImageRenamePreferences),
+ (subfolderPrefs, SubfolderPreferences),
+ (videoRenamePrefs, VideoRenamePreferences),
+ (videoSubfolderPrefs, VideoSubfolderPreferences))
+ for i, Prefs in tests:
+ p = Prefs(i, None)
+ p.checkPrefsForValidity()
except:
return False
return True
@@ -600,6 +678,8 @@ def checkPreferencesForValidity(imageRenamePrefs, subfolderPrefs, version=conf
try:
checkPreferenceValid(defn, imageRenamePrefs)
checkPreferenceValid(DICT_SUBFOLDER_L0, subfolderPrefs)
+ checkPreferenceValid(DICT_VIDEO_SUBFOLDER_L0, videoSubfolderPrefs)
+ checkPreferenceValid(DICT_VIDEO_RENAME_L0, videoRenamePrefs)
except:
return False
return True
@@ -694,6 +774,10 @@ def filterSubfolderPreferences(prefList):
class PrefError(Exception):
""" base class """
def unpackList(self, l):
+ """
+ Make the preferences presentable to the user
+ """
+
s = ''
for i in l:
if i <> ORDER_KEY:
@@ -718,7 +802,7 @@ class PrefValueInvalidError(PrefKeyError):
class PrefLengthError(PrefError):
def __init__(self, error):
- self.msg = _("These preferences are not well formed:") % self.unpackList(error) + "\n %s"
+ self.msg = _("These preferences are not well formed:") + "\n %s" % self.unpackList(error)
class PrefValueKeyComboError(PrefError):
def __init__(self, error):
@@ -785,6 +869,7 @@ class ImageRenamePreferences:
self.defaultPrefs = [FILENAME, NAME_EXTENSION, ORIGINAL_CASE]
self.defaultRow = self.defaultPrefs
self.stripForwardSlash = True
+ self.L1DateCheck = IMAGE_DATE #used in _getDateComponent()
@@ -819,19 +904,42 @@ class ImageRenamePreferences:
def setJobCode(self, job_code):
self.job_code = job_code
+
+ def _fixMangledDateTime(self, d):
+ """ Some EXIF dates are badly formed. Try to fix them """
+
+ _datetime = d.strip()
+ # remove any weird characters at the end of the string
+ while _datetime and not _datetime[-1].isdigit():
+ _datetime = _datetime[:-1]
+ _date, _time = _datetime.split(' ')
+ _datetime = "%s %s" % (_date.replace(":", "-") , _time.replace("-", ":"))
+ try:
+ d = datetime.datetime.strptime(_datetime, '%Y-%m-%d %H:%M:%S')
+ except:
+ d = None
+ return d
+
def _getDateComponent(self):
"""
- Returns portion of new image / subfolder name based on date time
+ Returns portion of new image / subfolder name based on date time.
+ If the date is missing, will attempt to use the fallback date.
"""
problem = None
- if self.L1 == IMAGE_DATE:
+
+ if self.L1 == self.L1DateCheck:
if self.L2 == SUBSECONDS:
d = self.photo.subSeconds()
- problem = _("Subsecond metadata not present in image")
+ problem = _("Subsecond metadata not present in photo")
+ if d == '00':
+ return ('', problem)
+ else:
+ return (d, None)
else:
d = self.photo.dateTime(missing=None)
- problem = _("%s metadata is not present in image") % self.L1.lower()
+ problem = _("%s metadata is not present") % self.L1.lower()
+
elif self.L1 == TODAY:
d = datetime.datetime.now()
elif self.L1 == YESTERDAY:
@@ -841,34 +949,33 @@ class ImageRenamePreferences:
raise("Date options invalid")
if d:
- if self.L2 <> SUBSECONDS:
-
- if type(d) == type('string'):
- # will be a string only if the date time could not be converted in the datetime type
- # try to massage badly formed date / times into a valid value
- _datetime = d.strip()
- # remove any weird characters at the end of the string
- while _datetime and not _datetime[-1].isdigit():
- _datetime = _datetime[:-1]
- _date, _time = _datetime.split(' ')
- _datetime = "%s %s" % (_date.replace(":", "-") , _time.replace("-", ":"))
- try:
- d = datetime.datetime.strptime(_datetime, '%Y-%m-%d %H:%M:%S')
- except:
- v = ''
- problem = _('Error in date time component. Value %s appears invalid') % ''
- return (v, problem)
-
+ # if format is not the standard floating point representation
+ # of a date time, there is a problem
+ if type(d) == type('string'):
+ # will be a string only if the date time could not be converted in the datetime type
+ # try to massage badly formed date / times into a valid value
+ d = self._fixMangledDateTime(d)
+ if d is None:
+ v = ''
+ problem = _('Error in date time component. Value %s appears invalid') % ''
+ return (v, problem)
+ else:
+ if self.fallback_date:
try:
- return (d.strftime(convertDateForStrftime(self.L2)), None)
+ d = datetime.datetime.fromtimestamp(self.fallback_date)
except:
v = ''
- problem = _('Error in date time component. Value %s appears invalid') % d
- return (v, problem)
+ problem = _('Error in date time component. Value %s appears invalid') % ''
+ return (v, problem)
else:
- return (d, None)
- else:
- return ('', problem)
+ return ('', problem)
+
+ try:
+ return (d.strftime(convertDateForStrftime(self.L2)), None)
+ except:
+ v = ''
+ problem = _('Error in date time component. Value %s appears invalid') % d
+ return (v, problem)
def _getFilenameComponent(self):
"""
@@ -894,11 +1001,11 @@ class ImageRenamePreferences:
filename = extension[1:]
else:
filename = ""
- problem = _("extension was specified but image name has no extension")
- elif self.L1 == IMAGE_NUMBER:
+ problem = _("extension was specified but filename does not have an extension")
+ elif self.L1 == IMAGE_NUMBER or self.L1 == VIDEO_NUMBER:
n = re.search("(?P<image_number>[0-9]+$)", name)
if not n:
- problem = _("image number was specified but image filename has no number")
+ problem = _("image or video number was specified but filename has no number")
else:
image_number = n.group("image_number")
@@ -971,7 +1078,7 @@ class ImageRenamePreferences:
md = self.L1.lower()
else:
md = ISO
- problem = _("%s metadata is not present in image") % md
+ problem = _("%s metadata is not present in photo") % md
return (v, problem)
@@ -1037,7 +1144,6 @@ class ImageRenamePreferences:
def _getDownloadsTodaySequenceNo(self):
problem = None
-
v = self._formatSequenceNo(self.sequences.getDownloadsTodayUsingCounter(self.sequenceCounter), self.L2)
return (v, problem)
@@ -1085,7 +1191,7 @@ class ImageRenamePreferences:
return (os.sep, None)
except:
v = ""
- problem = _("error generating name with component %s") % self.L2
+ problem = _("error generating name with component %s") % self.L0
return (v, problem)
def _getValuesFromList(self):
@@ -1093,10 +1199,11 @@ class ImageRenamePreferences:
yield (self.prefList[i], self.prefList[i+1], self.prefList[i+2])
- def _generateName(self, photo, existingFilename, stripCharacters, subfolder, stripInitialPeriodFromExtension, sequence):
+ def _generateName(self, photo, existingFilename, stripCharacters, subfolder, stripInitialPeriodFromExtension, sequence, fallback_date):
self.photo = photo
self.existingFilename = existingFilename
self.stripInitialPeriodFromExtension = stripInitialPeriodFromExtension
+ self.fallback_date = fallback_date
name = ''
problem = ''
@@ -1130,7 +1237,8 @@ class ImageRenamePreferences:
stripCharacters = False, subfolder=None,
stripInitialPeriodFromExtension=False,
sequencesPreliminary = True,
- sequence_to_use = None):
+ sequence_to_use = None,
+ fallback_date = None):
"""
Generate a filename for the photo in string format based on user prefs.
@@ -1150,7 +1258,7 @@ class ImageRenamePreferences:
sequence = 0
return self._generateName(photo, existingFilename, stripCharacters, subfolder,
- stripInitialPeriodFromExtension, sequence)
+ stripInitialPeriodFromExtension, sequence, fallback_date)
def generateNameSequencePossibilities(self, photo, existingFilename,
stripCharacters=False, subfolder=None,
@@ -1312,16 +1420,65 @@ class ImageRenamePreferences:
self._getPreferenceWidgets(self.prefsDefnL0, selection, widgets)
return widgets
+def getVideoMetadataComponent(video):
+ """
+ Returns portion of video / subfolder name based on the metadata
+
+ This is outside of a class definition because of the inheritence
+ hierarchy.
+ """
+
+ problem = None
+ if video.L1 == CODEC:
+ v = video.photo.codec()
+ elif video.L1 == WIDTH:
+ v = video.photo.width()
+ elif video.L1 == HEIGHT:
+ v = video.photo.height()
+ elif video.L1 == FPS:
+ v = video.photo.fps()
+ elif video.L1 == LENGTH:
+ v = video.photo.length()
+ else:
+ raise TypeError("Invalid metadata option specified")
+ if video.L1 in [CODEC]:
+ if video.L2 == UPPERCASE:
+ v = v.upper()
+ elif video.L2 == LOWERCASE:
+ v = v.lower()
+ if not v:
+ problem = _("%s metadata is not present in video") % video.L1
+ return (v, problem)
+
+class VideoRenamePreferences(ImageRenamePreferences):
+ def __init__(self, prefList, parent, fileSequenceLock=None, sequences=None):
+ self.prefsDefnL0 = DICT_VIDEO_RENAME_L0
+ self.defaultPrefs = [FILENAME, NAME_EXTENSION, ORIGINAL_CASE]
+ self.defaultRow = self.defaultPrefs
+ self.stripForwardSlash = True
+ self.L1DateCheck = VIDEO_DATE
+ ImageRenamePreferences.__init__(self, prefList, parent, fileSequenceLock, sequences)
+
+ def _getMetadataComponent(self):
+ """
+ Returns portion of video / subfolder name based on the metadata
+
+ Note: date time metadata found in _getDateComponent()
+ """
+ return getVideoMetadataComponent(self)
+
+
class SubfolderPreferences(ImageRenamePreferences):
def __init__(self, prefList, parent):
self.prefsDefnL0 = DICT_SUBFOLDER_L0
self.defaultPrefs = DEFAULT_SUBFOLDER_PREFS
self.defaultRow = [DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[0]]
self.stripForwardSlash = False
+ self.L1DateCheck = IMAGE_DATE
ImageRenamePreferences.__init__(self, prefList, parent)
def generateNameUsingPreferences(self, photo, existingFilename=None,
- stripCharacters = False):
+ stripCharacters = False, fallback_date = None):
"""
Generate a filename for the photo in string format based on user prefs.
@@ -1332,7 +1489,9 @@ class SubfolderPreferences(ImageRenamePreferences):
subfolders, problem = ImageRenamePreferences.generateNameUsingPreferences(
self, photo,
- existingFilename, stripCharacters, stripInitialPeriodFromExtension=True)
+ existingFilename, stripCharacters,
+ stripInitialPeriodFromExtension=True,
+ fallback_date=fallback_date)
# subfolder value must never start with a separator, or else any
# os.path.join function call will fail to join a subfolder to its
# parent folder
@@ -1394,11 +1553,29 @@ class SubfolderPreferences(ImageRenamePreferences):
return v
-class Sequences:
- """ Holds sequence numbers and letters used in generating filenames"""
- def __init__(self, downloadsToday, storedSequenceNo):
+class VideoSubfolderPreferences(SubfolderPreferences):
+ def __init__(self, prefList, parent):
+ SubfolderPreferences.__init__(self, prefList, parent)
+ self.prefsDefnL0 = DICT_VIDEO_SUBFOLDER_L0
+ self.defaultPrefs = DEFAULT_VIDEO_SUBFOLDER_PREFS
+ self.defaultRow = [DATE_TIME, VIDEO_DATE, LIST_DATE_TIME_L2[0]]
+ self.L1DateCheck = VIDEO_DATE
+ def _getMetadataComponent(self):
+ """
+ Returns portion of video / subfolder name based on the metadata
+
+ Note: date time metadata found in _getDateComponent()
+ """
+ return getVideoMetadataComponent(self)
+
+class Sequences:
+ """
+ Holds sequence numbers and letters used in generating filenames.
+ The same instance of this class is shared among all threads.
+ """
+ def __init__(self, downloadsToday, storedSequenceNo):
self.subfolderSequenceNo = {}
self.sessionSequenceNo = 1
self.sequenceLetter = 0
diff --git a/rapid/renamesubfolderprefstest.py b/rapid/renamesubfolderprefstest.py
index a689c5c..26afd97 100644
--- a/rapid/renamesubfolderprefstest.py
+++ b/rapid/renamesubfolderprefstest.py
@@ -35,6 +35,31 @@ class PreferenceTest (unittest.TestCase):
[FILENAME, EXTENSION, LOWERCASE]
)
+ video_name_test= (
+ [DATE_TIME, VIDEO_DATE, 'HHMMSS'],
+ [METADATA, CODEC, LOWERCASE],
+ [METADATA, FPS, ''],
+ )
+
+ video_name_test2= (
+ [DATE_TIME, VIDEO_DATE, 'HHMMSS',
+ METADATA, CODEC, LOWERCASE,
+ METADATA, FPS, ''],
+ )
+
+ video_name_test3= (
+ [FILENAME, VIDEO_NUMBER, IMAGE_NUMBER_4,
+ FILENAME, NAME_EXTENSION, LOWERCASE,
+ METADATA, FPS, ''],
+ )
+
+ video_subfolder_test= (
+ [DATE_TIME, TODAY, 'HHMMSS',
+ SEPARATOR, '', '',
+ METADATA, WIDTH, ''],
+ )
+
+
trueMetadataTest = ([FILENAME, EXTENSION, LOWERCASE, TEXT, '', '', METADATA, APERTURE, ''], [METADATA, APERTURE, '', TEXT, '', '', FILENAME, EXTENSION, LOWERCASE], )
falseMetadataTest = ([FILENAME, EXTENSION, LOWERCASE, METADATA, APERTURE, '', FILENAME, NAME, LOWERCASE],
@@ -52,6 +77,17 @@ class PreferenceTest (unittest.TestCase):
for pref in self.image_test:
result = checkPreferenceValid(DICT_IMAGE_RENAME_L0, pref)
self.assertEqual(result, True)
+
+ def testPrefVideoList(self):
+ for pref in self.video_name_test:
+ result = checkPreferenceValid(DICT_VIDEO_RENAME_L0, pref)
+ self.assertEqual(result, True)
+ for pref in self.video_name_test2:
+ result = checkPreferenceValid(DICT_VIDEO_RENAME_L0, pref)
+ self.assertEqual(result, True)
+ for pref in self.video_name_test3:
+ result = checkPreferenceValid(DICT_VIDEO_RENAME_L0, pref)
+ self.assertEqual(result, True)
def testSequencesList(self):
for pref in self.sequences_test:
@@ -68,8 +104,6 @@ class PreferenceTest (unittest.TestCase):
p = ImageRenamePreferences(i, None)
result = p.needImageMetaDataToCreateUniqueName()
self.assertEqual(result, False)
-
-
def testLargePrefList(self):
prefList = []
@@ -84,6 +118,11 @@ class PreferenceTest (unittest.TestCase):
for pref in self.subfolder_test:
result = checkPreferenceValid(DICT_SUBFOLDER_L0, pref)
self.assertEqual(result, True)
+
+ def testPrefVideoSubfolderList(self):
+ for pref in self.video_subfolder_test:
+ result = checkPreferenceValid(DICT_VIDEO_SUBFOLDER_L0, pref)
+ self.assertEqual(result, True)
def testDateTimeL2Length(self):
self.assertEqual(len(LIST_DATE_TIME_L2), len(DATE_TIME_CONVERT))
@@ -107,6 +146,12 @@ class BadPreferences(unittest.TestCase):
[DATE_TIME, None, None],
[DATE_TIME, '', ''],
)
+
+ bad_image_key2 = (
+ [FILENAME, VIDEO_NUMBER, IMAGE_NUMBER_4,
+ FILENAME, NAME_EXTENSION, LOWERCASE,
+ METADATA, APERTURE, ''],
+ )
bad_subfolder_key = ([FILENAME, NAME_EXTENSION, UPPERCASE],)
@@ -145,12 +190,18 @@ class BadPreferences(unittest.TestCase):
self.assertRaises(PrefKeyError, checkPreferenceValid,
DICT_IMAGE_RENAME_L0,
pref)
+ for pref in self.bad_image_key2:
+ self.assertRaises(PrefKeyError, checkPreferenceValid,
+ DICT_IMAGE_RENAME_L0,
+ pref)
+
def testBadImageValue(self):
for pref in self.bad_image_value:
self.assertRaises(PrefValueInvalidError, checkPreferenceValid,
DICT_IMAGE_RENAME_L0,
- pref)
+ pref)
+
def testBadSubfolderKey(self):
for pref in self.bad_subfolder_key:
@@ -186,5 +237,11 @@ class BadPreferences(unittest.TestCase):
s = SubfolderPreferences(pref, self)
self.assertRaises(PrefValueKeyComboError, s.checkPrefsForValidity)
+ def testBadVideoSubfolderCombo(self):
+
+ for pref in self.bad_subfolder_combos:
+ s = VideoSubfolderPreferences(pref, self)
+ self.assertRaises(PrefValueKeyComboError, s.checkPrefsForValidity)
+
if __name__ == "__main__":
unittest.main()
diff --git a/rapid/videometadata.py b/rapid/videometadata.py
new file mode 100755
index 0000000..210da33
--- /dev/null
+++ b/rapid/videometadata.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python
+# -*- coding: latin1 -*-
+
+### Copyright (C) 2007-10 Damon Lynch <damonlynch@gmail.com>
+
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+DOWNLOAD_VIDEO = True
+
+import os
+import datetime
+import subprocess
+import tempfile
+
+import gtk
+import media
+import paths
+
+try:
+ import kaa.metadata
+except ImportError:
+ DOWNLOAD_VIDEO = False
+
+VIDEO_THUMBNAIL_FILE_EXTENSIONS = ['thm']
+VIDEO_FILE_EXTENSIONS = ['avi', 'mov', 'mp4']
+
+if DOWNLOAD_VIDEO:
+
+ try:
+ subprocess.check_call(["ffmpegthumbnailer", "-h"], stdout=subprocess.PIPE)
+ ffmpeg = True
+ except:
+ ffmpeg = False
+
+
+ def version_info():
+ return str(kaa.metadata.VERSION)
+
+ class VideoMetaData():
+ def __init__(self, filename):
+ self.info = kaa.metadata.parse(filename)
+ self.filename = filename
+ self.filmstrip = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/filmstrip-100x75.xpm'))
+ self.FILMSTRIP_WIDTH = 100
+ self.FILMSTRIP_HEIGHT = 75
+
+ def rpd_keys(self):
+ return self.info.keys()
+
+ def _get(self, key, missing, stream=None):
+ if stream != None:
+ v = self.info['video'][stream][key]
+ else:
+ v = self.info[key]
+ if v:
+ return str(v)
+ else:
+ return missing
+
+ def dateTime(self, missing=''):
+ dt = self._get('timestamp', missing=None)
+ if dt:
+ try:
+ return datetime.datetime.fromtimestamp(self.info['timestamp'])
+ except:
+ return missing
+ else:
+ return missing
+
+ def codec(self, stream=0, missing=''):
+ return self._get('codec', missing, stream)
+
+ def length(self, missing=''):
+ l = self._get('length', missing)
+ try:
+ l = '%.0f' % float(l)
+ except:
+ pass
+ return l
+
+ def width(self, stream=0, missing=''):
+ return self._get('width', missing, stream)
+
+ def height(self, stream=0, missing=''):
+ return self._get('height', missing, stream)
+
+ def framesPerSecond(self, stream=0, missing=''):
+ fps = self._get('fps', missing, stream)
+ try:
+ fps = '%.0f' % float(fps)
+ except:
+ pass
+ return fps
+
+ def fourcc(self, stream=0, missing=''):
+ return self._get('fourcc', missing, stream)
+
+ def getThumbnailData(self, size, tempWorkingDir):
+ thm = media.getVideoThumbnailFile(self.filename)
+ if thm:
+ thumbnail = gtk.gdk.pixbuf_new_from_file_at_size(thm, size, size)
+ if thumbnail.get_width() <> self.FILMSTRIP_WIDTH or thumbnail.get_height() <> self.FILMSTRIP_HEIGHT:
+ thumbnail = thumbnail.scale_simple(self.FILMSTRIP_WIDTH, self.FILMSTRIP_HEIGHT, gtk.gdk.INTERP_BILINEAR)
+
+ self.filmstrip.composite(thumbnail, 0, 0, self.filmstrip.props.width, self.filmstrip.props.height, 0, 0, 1.0, 1.0, gtk.gdk.INTERP_HYPER, 255)
+ else:
+ if ffmpeg:
+ tmp = tempfile.NamedTemporaryFile(dir=tempWorkingDir, prefix="rpd-tmp")
+ tmp.close()
+
+ thm = os.path.join(tempWorkingDir, tmp.name)
+
+ try:
+ subprocess.check_call(['ffmpegthumbnailer', '-i', self.filename, '-t', '10', '-f', '-o', thm, '-s', str(size)])
+ thumbnail = gtk.gdk.pixbuf_new_from_file_at_size(thm, size, size)
+ os.unlink(thm)
+ except:
+ thumbnail = None
+ else:
+ thumbnail = None
+ return thumbnail
+
+class DummyMetaData():
+ """
+ Class which gives metadata values for an imaginary video.
+
+ Useful for displaying in preference examples etc. when no video is ready to
+ be downloaded.
+
+ See VideoMetaData class for documentation of class methods.
+ """
+ def __init__(self):
+ pass
+
+ def dateTime(self, missing=''):
+ return datetime.datetime.now()
+
+ def codec(self, stream=0, missing=''):
+ return 'H.264 AVC'
+
+ def length(self, missing=''):
+ return '57'
+
+ def width(self, stream=0, missing=''):
+ return '1920'
+
+ def height(self, stream=0, missing=''):
+ return '1080'
+
+ def framesPerSecond(self, stream=0, missing=''):
+ return '24'
+
+ def fourcc(self, stream=0, missing=''):
+ return 'AVC1'
+
+
+if __name__ == '__main__':
+ import sys
+
+
+ if (len(sys.argv) != 2):
+ print 'Usage: ' + sys.argv[0] + ' path/to/video/containing/metadata'
+ sys.exit(0)
+
+ else:
+ m = VideoMetaData(sys.argv[1])
+ dt = m.dateTime()
+ if dt:
+ print dt.strftime('%Y%m%d-%H:%M:%S')
+ print "codec: %s" % m.codec()
+ print "%s seconds" % m.length()
+ print "%sx%s" % (m.width(), m.height())
+ print "%s fps" % m.framesPerSecond()
+ print "Fourcc: %s" % (m.fourcc())
+