Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Mindaugas Vaitiekūnas committed Jul 23, 2023
2 parents 0f4ff0e + a0112f5 commit 293bee5
Show file tree
Hide file tree
Showing 31 changed files with 1,093 additions and 19 deletions.
18 changes: 16 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
.vscode
test-data.txt
clipnotify
.idea
# TODO remove clipnotify
clipnotify
docs/notes

# virtualenv artifacts
.eggs
.venv*

# runtime artifacts
**/__pycache__

# build artifacts
build
dist
*.spec
4 changes: 0 additions & 4 deletions Makefile

This file was deleted.

Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icon_flash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
174 changes: 174 additions & 0 deletions builder.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/usr/bin/env bash

# Usage examples:
# `install arm python3.10` or `install intel python3.10-intel64`
# `build arm` or `build intel`

install() {(set -e
# Prepare virtualenv for given architecture

arch=$1 # architecture name, `arm` | `intel`
pythonExec=$2 # python executable name, e.g. `python3.10` or `python3.10-intel64`

_validateArchArg $arch

if [ -z "$pythonExec" ]; then
_logError 'Python executable must be provided as argument (e.g. "python3.10" or "python3.10-intel64")'
exit 1
fi

_validateExecutableArchitecture $arch $pythonExec

venvPath=".venv-$arch"
_log "Installing virtualenv to \"$venvPath\""

$pythonExec -m venv .venv-$arch --clear --copies

source "$venvPath/bin/activate"

pip install wheel
pip install -r requirements_macos.txt

_log "Successfully installed virtualenv to \"$venvPath\""
)}

build() {(set -e
arch=$1 # architecture name, `arm` | `intel`

_validateArchArg $arch
fullArchName=$(_getFullArchName $arch)
venvPath=".venv-$arch"
distPath="dist/$fullArchName"

source "$venvPath/bin/activate"

rm -rf ./$distPath ./build/ ./*.spec

_log "Starting build for $fullArchName"

$venvPath/bin/pyinstaller \
--clean \
--name "Statusbar Converter" \
--onedir \
--windowed \
--add-data '../../assets:assets' \
--add-data '../../config:config' \
--distpath "$distPath" \
--workpath "$distPath/build" \
--specpath "$distPath" \
--icon '../../assets/icon.png' \
--target-arch "$fullArchName" \
--osx-bundle-identifier 'com.mindaugasw.statusbar_converter' \
start.py

_log "Successfully built for $fullArchName in $distPath"

_createZip $fullArchName
)}

_createDmg() {(set -e
# No longer used. .dmg image triggers additional safety measures for unsigned apps:
# - Chrome warns about unsafe file when downloading. .zip avoids this problem
# - Still need to manually un-quarantine app with `xattr -d com.apple.quarantine path.app`
# For .dmg it's always needed for download. .zip seems to avoid quarantine
# after download but only on the same machine that it was built on

# `create-dmg` in path is required
# `brew install create-dmg`

arch=$1 # full architecture name, `arm64` | `x86_64`

_log "Packing into dmg image for $arch"

fileName="Statusbar_Converter_macOS_$arch.dmg"

cd dist/$arch

# create-dmg includes all files in the directory. So we copy only the needed stuff to a new directory
rm -rf dmg/ ./*.dmg
mkdir dmg
cp -r 'Statusbar Converter.app' 'dmg/Statusbar Converter.app'

create-dmg \
--volname 'Statusbar Converter' \
--icon-size 80 \
--text-size 14 \
--icon 'Statusbar Converter.app' 190 0 \
--app-drop-link 0 0 \
--hide-extension 'Statusbar Converter.app' \
$fileName \
dmg/

rm -rf dmg/

_log "Successfully packed into dmg image \"$fileName\""
)}

_createZip() {(set -e
arch=$1 # full architecture name, `arm64` | `x86_64`

cd "dist/$arch"
fileName="Statusbar_Converter_macOS_$arch.app.zip"
_log "Compressing into zip for $arch"

zip -r "$fileName" "Statusbar Converter.app"

_log "Successfully compressed into \"$fileName\""
)}

_validateExecutableArchitecture() {(set -e
arch=$1 # architecture name, `arm` | `intel`
pythonExec=$2 # python executable name, e.g. `python3.10` or `python3.10-intel64`

_validateArchArg $arch

platform=$($pythonExec -c 'import platform; print(platform.platform())')
needle=$(_getFullArchName $arch)

if [[ "$platform" != *"$needle"* ]]; then
_logError "Requested architecture ($arch) does not match provided python executable ($platform)"
exit 1
else
_log "Requested architecture ($arch) matches provided python executable ($platform)"
fi
)}

_validateArchArg() {(set -e
# $1 - architecture name

if [ "$1" != 'arm' ] && [ "$1" != 'intel' ]; then
_logError 'Argument must be either "arm" or "intel"'
exit 1
fi
)}

_getFullArchName() {(set -e
# $1 - architecture name

if [ "$1" == 'arm' ]; then
printf 'arm64'
elif [ "$1" == 'intel' ]; then
printf 'x86_64'
else
_logError 'Argument must be either "arm" or "intel"'
exit 1
fi
)}

_log() {(set -e
yellowCode='\033[0;33m'
resetCode='\033[0m'

printf "$yellowCode> $1$resetCode\n"
)}

_logError() {(set -e
redCode='\033[0;31m'
resetCode='\033[0m'

printf "$redCode\nERROR: $1$resetCode\n"
)}

# This will call function name from the first argument
# See: https://stackoverflow.com/a/16159057/4110469
"$@"
61 changes: 61 additions & 0 deletions config/config.app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# If enabled, text in the statusbar will be cleared after copying anything else
clear_on_change: false

# Automatically clear text in the status bar after this time, in seconds.
# 0 to disable automatic clearing.
clear_after_time: 300

# Parse text on highlighting it, without having to copy it.
# Supported only on Linux, ignored on macOS. # TODO
# Highlight detection may not work in some apps, in which case you can still copy the text to manually trigger timestamp update
update_on_highlight: true # TODO

# If enabled, will shortly flash status bar icon on timestamp change
flash_icon_on_change: true

# Formatting template for main text on the statusbar.
# Will use first template found where key is less than relative time difference
# in seconds.
#
# Templates must be ordered ascending by time difference in their key.
#
# Templates support all standard strftime() codes:
# https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
# and these custom codes for relative time:
# - {ts} - unix timestamp. Milliseconds (if copied timestamp with milliseconds) will be separated with a dot for better readability.
# - {ts_ms} - unix timestamp. Milliseconds won't be separated to allow copying a valid timestamp.
# - {r_int} - relative time with integer number, e.g. '5 h ago'.
# - {r_float} - relative time with float number, e.g. '5.5 h ago'.
# TODO name this somehow better
format_icon:
# Up to 60 s, e.g. "1619295908 - 59 s ago"
60: "{ts} - {r_int}"
# 1 min - 10 min, e.g. "1619295908 - 5 min ago"
600: "{ts} - {r_int}"
# 10 min - 1 hour, e.g. "1619295908 - 29 min ago (15:10)"
3600: "{ts} - {r_float} (%H:%M)"
# 1 hour - 1 day, e.g. "1619295908 - 5.5 h ago (12:00)"
86400: "{ts} - {r_float} (%H:%M)"
# 1 day - 1 month, e.g. "1619295908 - 5.5 days ago (08-05 12:00)"
2678400: "{ts} - {r_float} (%m-%d %H:%M)"
# 1 month - 1 year, e.g. "1619295908 - 9.2 months ago"
31536000: "{ts} - {r_float} (%m-%d %H:%M)"
# 1 year - 75 years, e.g. "1619295908 - 5.8 years ago ('15)"
2365200000: "{ts} - {r_float} ('%y)"
# Default if no other format matches. Must be last element in the list
default: "{ts} - {r_float}"

# Formatting template to use for menu items under "Last timestamp".
# Supports the same formats as in "format_icon" configuration.
menu_items_last_timestamp:
Last timestamp: "{ts_ms}"
Last datetime: "%Y-%m-%d %H:%M:%S"

# Formatting template to use for menu items under "Current timestamp".
# Supports the same formats as in "format_icon" configuration.
menu_items_current_timestamp:
Current timestamp: "{ts_ms}"
Current datetime: "%Y-%m-%d %H:%M:%S"

# If enabled, will log additional info to the console
debug: false
7 changes: 7 additions & 0 deletions config/config.user.example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Warning: user config will not be updated automatically and may break new app
# versions in the future.
#
# All supported configuration can be found at
# https://github.com/mindaugasw/statusbar-converter/tree/master/config/config.app.yml
#
# After editing configuration the application must be restarted.
35 changes: 22 additions & 13 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
# Timestamp tray converter

It's a small tool to easily convert timestamp to human-readable time. Just highlight the timestamp and it will automatically show as time in the tray. See demo below:

![demo](/docs/demo.gif)

## Usage
Currently works only on Linux. Requires Python and xsel package.
- `git clone https://github.com/mindaugasw/timestamp-tray-converter.git`
- `cd timestamp-tray-converter`
- `make`
- Optionally change app configuration in `timestamp-tray-converter.py`
- Run `python ./timestamp-tray-converter.py` or add it to your startup items
# Statusbar Converter

A small tool to easily convert timestamp to human-readable time. Just copy the
timestamp and it will automatically show as time in the statusbar:

![demo](/docs/demo-2.gif)


## Installation

- Download the latest release from [GitHub](https://github.com/mindaugasw/statusbar-converter/releases)
- Extract zip
- On macOS you must manually remove quarantine attribute, because the app is not signed:
`xattr -d com.apple.quarantine /Applications/Statusbar\ Converter.app/`
- Start the app. A new icon will appear on the statusbar
- To automatically launch the app on boot, go to System Preferences, search for `Login items` and add the app
- _Tip:_ on macOS you can change icon order on the statusbar with Cmd-click


## Building locally

[See documentation for building locally.](/docs/building.md)
19 changes: 19 additions & 0 deletions docs/building.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Building locally

Install `python3.10`

> If you want to build `x86_64` architecture app on Apple Silicon, you need Python
> with `universal2` support from [python.org](https://www.python.org/downloads/release/python-31011/).
> Python from Homebrew will support only your current architecture.
Create virtualenv with:
- `./builder.sh install arm python3.10` (`arm64` native)
- `./builder.sh install intel python3.10` (`x86_64` native)
- `./builder.sh install intel python3.10-intel64` (building `x86_64` on `arm64`)

Run app:
- `source .venv-arm/bin/activate`
- `python start.py`

Build distributable:
- `./builder.sh build arm`
Binary file added docs/demo-2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions requirements_macos.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pasteboard==0.3.3
PyYAML==6.0
rumps==0.4.0
pyinstaller
# Pillow is needed for pyinstaller to be able to convert .png image to .icns to allow to use as macOS file icon
Pillow
34 changes: 34 additions & 0 deletions src/Entity/Event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Event(list):
"""Event subscription.
A list of callable objects. Calling an instance of this will cause a
call to each item in the list in ascending order by index.
Source: https://stackoverflow.com/a/2022629/4110469
Example Usage:
>>> def f(x):
... print 'f(%s)' % x
>>> def g(x):
... print 'g(%s)' % x
>>> e = Event()
>>> e()
>>> e.append(f)
>>> e(123)
f(123)
>>> e.remove(f)
>>> e()
>>> e += (f, g)
>>> e(10)
f(10)
g(10)
>>> del e[0]
>>> e(2)
g(2)
"""
def __call__(self, *args, **kwargs):
for f in self:
f(*args, **kwargs)

def __repr__(self):
return "Event(%s)" % list.__repr__(self)
Loading

0 comments on commit 293bee5

Please sign in to comment.