Skip to content

Commit

Permalink
pack the project into a pip package
Browse files Browse the repository at this point in the history
  • Loading branch information
nschepsen committed Dec 15, 2022
1 parent 19da121 commit e47aecf
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 127 deletions.
74 changes: 31 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,58 @@ pip install -r requirements.txt

## USAGE ##

tnmake [-h] -i path [-o filename] [-q percentage] [-w px] [-c annotation] [-g layout] [-e {bmp,jpg,png}] [-f path] [-v] [-V]
usage: tnmake -i path [-o path] [-w px] [-c annotation] [--grid layout] [-e {bmp,jpg,png}] [-q x] [-f path] [-s px]

* -h, --help
Thumbnail!MAKER creates thumbnails with some additional information

show this help message and exit
options:
-h, --help

* -i path, --input path
* show this help message and exit

set video file path
-i path, --input path

* -o filename, --output filename
* set video filepath

set custom output filename
-o path, --output path

* -q percentage, --quality percentage
* force a custom output filepath

set output image quality (default: 100)
-w px, --width px

* -w px, --width px
* set width of the output image

set width of output image
--comment annotation

* -c annotation, --comment annotation
* append a thumbnail annotation

add a comment as a thumbnail annotation
--grid layout

* -g layout, --grid layout
* set layout of the output thumbnail

set layout of a resulting thumbnail
-e {bmp,jpg,png}, --extension {bmp,jpg,png}

* -e {bmp,jpg,png}, --extension {bmp,jpg,png}
* choose the output extension (default: "jpg")

choose the output extension (default: "jpg")
-q x, --quality x

* -f path, --font path
* set quality, affects lossy image formats only

set path to a fontfile (default: "Mono Input Condensed (Light, Italic)")
-f path, --font path

* -v, --verbose
* select an available font or pass a path to a desired fontfile

enable verbose mode
-s px, --size px

* -V, --version
* set desired fontsize (default: 13px)

show program's version number and exit
-v, --verbose

* enable verbose mode

-V, --version

* show program's version number and exit


### EXAMPLES ###
Expand All @@ -73,30 +80,11 @@ show program's version number and exit
tnmake -i "Blade Runner (1982) Original.mkv" -w 750 -c "This is useful for adding small annotations (such as text labels)"
```

<details>
<summary>Example 1: Edgar Wallace (1964) Der Hexer</summary>

![Edgar Wallace (1964) Der Hexer](images/v0.2.0-example.0.jpg)
</details>

<details>
<summary>Example 2: Babylon 5 (S01E01)</summary>

![Babylon 5 (S01E01)](images/v0.2.0-example.1.jpg)
</details>

<details>
<summary>Example 3: Blade Runner (1982) Original</summary>

![Blade Runner (1982) Original](images/v0.2.0-example.2.jpg)

</details>

## CHANGELOG ##

### Thumbnail!MAKER 0.2.0, updated @ 2022-11-03 ###

* add a `pip` installable package
* pack the project into a `pip` package ([visit](https://pypi.org/project/tnmake/))

### Thumbnail!MAKER 0.1.1, updated @ 2020-01-14 ###

Expand Down
Binary file removed images/v0.2.0-example.0.jpg
Binary file not shown.
Binary file removed images/v0.2.0-example.1.jpg
Binary file not shown.
Binary file removed images/v0.2.0-example.2.jpg
Binary file not shown.
Binary file added samples/v0.2.0-example.0.jpg
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 samples/v0.2.0-example.1.jpg
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 samples/v0.2.0-example.2.jpg
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 samples/v0.2.0-example.3.jpg
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 samples/v0.2.0-example.4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 30 additions & 23 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
from setuptools import setup, find_packages

setup(
name='tnmake', # Thumbnail!MAKER
version='0.2.0',
license='GPLv3+',
description='Thumbnail!MAKER creates for you customisable thumbnails with some additional tech information',
package_dir={'':'src'},
packages=find_packages(where='src'),
author='nschepsen',
author_email='[email protected]',
url='https://github.com/nschepsen/thumbnail-maker',
keywords='library, video, tools',
entry_points={
# load the project description

with open('README.md', 'r') as fh:

ld = fh.read()

# set up the pip pkg installer

setup(name = 'tnmake', # Thumbnail!MAKER
version = '0.2.0',
license = 'GPLv3+',
description = 'Thumbnail!MAKER creates customisable thumbnails',
package_dir = {'':'src'},
packages = find_packages(where='src'),
install_requires = ['python-iso639', 'python-rapidjson>=1.6'],
author = 'nschepsen',
author_email = '[email protected]',
# github
keywords = 'library, video, tools',
# module's metadata
entry_points = {
'console_scripts': ['tnmake=tnmake.main:main']},
classifiers=[
'Development Status :: 4 - Beta'
classifiers = [
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: End Users/Desktop',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)'
'Natural Language :: English',
'Natural Language :: German',
'Natural Language :: Italian',
'Natural Language :: Russian',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 3',
'Topic :: Desktop Environment',
'Topic :: Home Automation',
'Topic :: Multimedia :: Video :: Display', 'Topic :: Utilities'
]
'Topic :: Multimedia :: Video', 'Topic :: Utilities'
],
long_description = ld, # long description
long_description_content_type = 'text/markdown',
url = 'https://github.com/nschepsen/thumbnail-maker' # repo
)

# Thumbnail!MAKER creates customisable thumbnails and adds some tech details in the picture's header
# Thumbnail!MAKER creates customisable thumbnails (ffmpeg wrapper)
Binary file removed src/tnmake/ColaborateLight.otf
Binary file not shown.
Binary file removed src/tnmake/InputMonoCondensedLightItalic.ttf
Binary file not shown.
84 changes: 45 additions & 39 deletions src/tnmake/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,68 +27,74 @@ def main():
Here is the ENTRYPOINT for `pip` and `standalone` versions
'''

parser = ArgumentParser(description=f'{__project__} creates customisable thumbnails with some additional information')
parser = ArgumentParser(description = f'{__project__} creates thumbnails with some additional information')
# add cli arguments
parser.add_argument(
'-i', '--input',
required=True, # pass a filepath
help=f'set video file path ',
metavar='path',
type=apt_path_exists) # movie
required = True,
help = f'set video filepath',
metavar = 'path',
type = apt_path_exists)
parser.add_argument(
'-o', '--output',
help='set custom output filename',
metavar='filename')
help = 'force a custom output filepath',
metavar = 'path')
# type=abspath,
# default=getcwd()) # use a current working directory as default
parser.add_argument(
'-q', '--quality',
type=int,
help='set output image quality (default: 100)',
metavar='percentage',
default=100) # quality gets over the space usage
# default=getcwd()) # use the current working directory as default
parser.add_argument(
'-w', '--width',
type=int,
help='set width of output image',
metavar='px',
default=1150)
type = int,
help = 'set width of the output image',
metavar = 'px',
default = 1150)
parser.add_argument(
'-c', '--comment',
type=str,
help='add a comment as a thumbnail annotation',
metavar='annotation',
default='') # let it empty if you won't left any commments
'--comment',
type = str,
help = 'append a thumbnail annotation',
metavar = 'annotation',
default = '') # let it empty if you won't append any commment
parser.add_argument(
'-g', '--grid',
type=str,
help='set layout of a resulting thumbnail',
metavar='layout',
default='3x8') # predefined layout consists of 3 cols & 8 rows
'--grid',
type = str,
help = 'set layout of the output thumbnail',
metavar = 'layout',
default = '3x8') # predefined layout consists of 3 cols & 8 rows
parser.add_argument(
'-e', '--extension',
help='choose the output extension (default: "jpg")',
choices=['bmp', 'jpg', 'png'],
default='jpg')
help = 'choose the output extension (default: "jpg")',
choices = ['bmp', 'jpg', 'png'],
default = 'jpg')
parser.add_argument(
'-q', '--quality',
type = int,
help = 'set quality, affects lossy image formats only',
metavar = 'x',
default = 100) # quality gets over the space usage
parser.add_argument(
'-f', '--font',
type=str,
help='set path to a fontfile (default: "Mono Input Condensed (Light, Italic)")',
metavar='path')
type = str,
help = 'select an available font or pass a path to a desired fontfile',
metavar = 'path')
parser.add_argument(
'-s', '--size',
type = int,
help = 'set desired fontsize (default: 13px)',
metavar = 'px')
parser.add_argument(
'-v', '--verbose',
action='store_true',
default=False,
help='enable verbose mode')
action = 'store_true',
default = False,
help = 'enable verbose mode')
parser.add_argument(
'-V', '--version',
action='version',
version=f'{__project__} v{__version__}')
action = 'version',
version = f'{__project__} v{__version__}')
args = parser.parse_args() # evaluate all arguments and pass 'em through
if args.verbose:
logger.setLevel(10)
options = {
'font': args.font, # set defaults in class itself
'size': args.size,
'layout': args.grid, # 3x8
'comment': args.comment,
'ext': args.extension,
Expand Down
45 changes: 23 additions & 22 deletions src/tnmake/tnmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,19 @@ def __init__(self, options: dict):
self.options = {
'comment': options.get('comment').strip(),
'extension': options.get('ext', 'jpg'),
'font': options.get('font') if isfile(str(options.get('font', 'd'))) else join(self.fpath, 'InputMonoCondensedLightItalic.ttf'),
'fontsize': 13,
'font': options.get('font') or '', # default system font
'fontsize': options.get('size') or 13,
'grid': options.get('layout', f'3x8'),
'quality': options.get('quality', 100),
'tfont': join(self.fpath, 'ColaborateLight.otf'),
# 'tfont': join(self.fpath, 'ColabLig.otf'), # Personal Use Only
'width': options.get('width', 1150)
}
# DBG # -----------------------------------------------------------------------
for k, v in self.options.items():
# dump it
logger.debug(
f'"{k}": {v if k not in ["font", "tfont"] else v.split(chr(47))[-1]}')
for k, v in self.options.items(): # dump options
logger.debug(f'"{k}": {v}') # for debug purposes
# --- # -----------------------------------------------------------------------

def perform(self, video: str, output=None):
def perform(self, video: str, output = None):

filename, ext = (lambda t: (t[0], t[1][1:]))(splitext(basename(video)))
metadata = loads(
Expand Down Expand Up @@ -102,6 +100,7 @@ def perform(self, video: str, output=None):
bitrate = str(floordiv(int(bitrate), 1000))
# handle video codec type specific tags
if stream['codec_type'] == 'video':
self.options['pointsize'] = truediv(stream['height'], 4.4)
# try to get video resolution
resolution = f'{stream["width"]}x{stream["height"]}'
# try to get display aspect ratio, e.g. 16:9
Expand Down Expand Up @@ -131,11 +130,13 @@ def perform(self, video: str, output=None):
for s in streams[:-1]:
for x in s:
logger.debug(f'"{x}"')
logger.debug(f'"SubTitles: {", ".join(streams[2])}"')
logger.debug(f'"Subtitles: {", ".join(streams[2]) or "Not Present"}"')
# --- # -----------------------------------------------------------------------
# PART 2: Snapshot Taking
shots = (lambda x: x[0]*x[1])(list(map(int, self.options['grid'].split('x'))))
framelist = []
if not output or output.endswith(chr(47)):
output = f'{output or ""}{filename}'
thumbnail = join(getcwd(), f'{output or filename}.bmp')
for i in range(shots):
step = floordiv(duration, shots)
Expand All @@ -145,37 +146,37 @@ def perform(self, video: str, output=None):
# append a frame id to the frame list
framelist.append(join(getcwd(), f'frame{frame:06d}.bmp'))
call(['ffmpeg', '-ss', str(frame), '-v', 'error', '-i', video, '-vframes', '1', framelist[-1], '-y'])
call(['convert',
framelist[-1],
'-font', self.options.get('tfont'),
# '-undercolor', 'white',
'-pointsize', '200',
call(['convert', framelist[-1],
# '-undercolor', 'white',
'-pointsize', f'{self.options["pointsize"]:.0f}',
'-gravity', 'South',
'-stroke', 'rgba(0,0,0,0.50)',
# '-strokewidth', '1',
'-stroke', 'rgba(0,0,0,0.750)',
# '-strokewidth', '1',
'-fill', 'rgba(255,255,255,0.20)',
'-annotate', '+0+30', f'{str(timedelta(seconds=frame)).zfill(8)}',
framelist[-1]])
# concate screenshots to a pre-defined grid layout
call(['montage', '-tile', self.options.get('grid'), '-geometry', '+2+2', *framelist, thumbnail])
call(['montage', '-tile', self.options.get('grid'), '-geometry', '+3+3', *framelist, thumbnail])
# adjust the thumbnail width to a value you prefere
call(['mogrify', '-resize', str(self.options.get('width')), thumbnail])
# create an annotation containing tech information
n, st_string = len(streams[StreamType.SubTitle]), 'Not Present'
if n:
st_string = ', '.join(streams[StreamType.SubTitle][:3]) + (f' and {n - 3} more' if n > 3 else '')
st_string = ', '.join(streams[2][:3]) + (f' and {n - 3} more' if n > 3 else '')
annotation = (
f'Filename: {filename}.{ext}\n'
f'{chr(10).join([i for j in streams[:2] for i in j])}\n' # video & audio streams
f'Duration: {str(timedelta(seconds=duration)).zfill(8)}, ' # hh:mm:ss
f'Size: {filesize:,} Bytes ({hrunits(filesize)})'
f'{chr(10) if len(streams[StreamType.SubTitle]) > 2 else ", "}'
f'SubTitles: {st_string}'
f'Subtitles: {st_string}'
f'{chr(10) + "Comment: " + self.options.get("comment") if self.options.get("comment") else ""}')
# render annotation string ('annotation.bmp')
call(['convert',
'-font', self.options.get('font'),
'-density', '288',
convert = ['convert']
if self.options.get('font'):
convert.extend(['-font', self.options.get('font')])
call([*convert,
'-density', '300', # 288
'-resize', '25%',
'-pointsize', str(self.options.get('fontsize')),
f'label:{annotation}',
Expand Down

0 comments on commit e47aecf

Please sign in to comment.