1- import functools
21import json
32import os
43from abc import ABCMeta
54from abc import abstractmethod
65from datetime import timedelta
76from distutils .dir_util import copy_tree
8- from distutils .dir_util import create_tree
97from distutils .dir_util import remove_tree
108
119from PIL import Image
1210
11+ from .pathtools import ensure_tree
12+ from .pathtools import extract_name
13+ from .pathtools import metadata_path
14+
1315
1416def register_thumbnail (typename ):
15- """Register a new thumbnail formatter to the factory."""
17+ """Register a new type of thumbnail generator into the factory."""
1618
1719 def _register_factory (cls ):
1820 if not issubclass (cls , Thumbnail ):
19- raise ValueError ("The formatter must implement the Thumbnail interface." )
21+ raise ValueError ("%s should be a Thumbnail." % cls . __name__ )
2022
2123 cls .extension = typename
2224 ThumbnailFactory .thumbnails [typename ] = cls
@@ -30,7 +32,7 @@ class ThumbnailExistsError(Exception):
3032
3133
3234class Thumbnail (metaclass = ABCMeta ):
33- """Any thumbnail describing format should implement the base Formatter ."""
35+ """Any thumbnail describing format should implement the base Thumbnail ."""
3436
3537 extension = None
3638
@@ -39,73 +41,63 @@ def __init__(self, video, base, skip, output):
3941 self .base = base
4042 self .skip = skip
4143 self .output = output
44+ self .thumbnail_dir = self .calc_thumbnail_dir ()
45+ self .metadata_path = self ._get_metadata_path ()
4246 self ._perform_skip ()
4347 self .extract_frames ()
4448
49+ def _get_metadata_path (self ):
50+ """Initiates the name of the thumbnail metadata file."""
51+ return metadata_path (self .filepath , self .output , self .extension )
52+
4553 def _perform_skip (self ):
46- """Raises ThumbnailExistsError to skip."""
47- if os .path .exists (self .get_metadata_path () ) and self .skip :
54+ """Checks the file existence and decide whether to skip or not ."""
55+ if os .path .exists (self .metadata_path ) and self .skip :
4856 raise ThumbnailExistsError
49- basedir , file = os .path .split (self .get_metadata_path ())
50- create_tree (basedir , [file ])
57+ ensure_tree (self .metadata_path )
5158
5259 def __getattr__ (self , item ):
53- """Delegate all other attributes to the video."""
60+ """Delegates all other attributes to the video."""
5461 return getattr (self .video , item )
5562
56- def get_metadata_path (self ):
57- """Return the name of the thumbnail file."""
58- return self .metadata_path (self .filepath , self .output , self .extension )
59-
60- @staticmethod
61- @functools .cache
62- def metadata_path (path , out , fmt ):
63- """Calculate the thumbnail metadata output path."""
64- out = os .path .abspath (out or os .path .dirname (path ))
65- filename = os .path .splitext (os .path .basename (path ))[0 ]
66- return os .path .join (out , "%s.%s" % (filename , fmt ))
67-
6863 @abstractmethod
69- def thumbnail_dir (self ):
70- """Creates and returns the thumbnail's output directory."""
64+ def calc_thumbnail_dir (self ):
65+ """Calculates and returns the thumbnail's output directory."""
7166
7267 @abstractmethod
7368 def prepare_frames (self ):
74- """Prepare the thumbnails before generating the output."""
69+ """Prepares the thumbnail frames before generating the output."""
7570
7671 @abstractmethod
7772 def generate (self ):
78- """Generate the thumbnails for the given video."""
73+ """Generates the thumbnail metadata for the given video."""
7974
8075
8176class ThumbnailFactory :
82- """A factory for creating thumbnail formatter ."""
77+ """A factory for creating a thumbnail for a particular format ."""
8378
8479 thumbnails = {}
8580
8681 @classmethod
8782 def create_thumbnail (cls , typename , * args , ** kwargs ) -> Thumbnail :
88- """Create a new thumbnail formatter by the given typename."""
83+ """Create a Thumbnail instance by the given typename."""
8984 try :
9085 return cls .thumbnails [typename ](* args , ** kwargs )
9186 except KeyError :
92- raise ValueError ("The formatter type '%s' is not registered." % typename )
87+ raise ValueError ("The thumbnail type '%s' is not registered." % typename )
9388
9489
9590@register_thumbnail ("vtt" )
9691class ThumbnailVTT (Thumbnail ):
9792 """Implements the methods for generating thumbnails in the WebVTT format."""
9893
99- def thumbnail_dir (self ):
100- basedir = self .output or os .path .dirname (self .filepath )
101- create_tree (os .path .abspath (basedir ), [self .filepath ])
102- return os .path .abspath (basedir )
94+ def calc_thumbnail_dir (self ):
95+ return ensure_tree (self .output or os .path .dirname (self .filepath ), True )
10396
10497 def prepare_frames (self ):
10598 thumbnails = self .thumbnails (True )
10699 master = Image .new (mode = "RGBA" , size = next (thumbnails ))
107- master_name = os .path .splitext (os .path .basename (self .filepath ))[0 ]
108- master_path = os .path .join (self .thumbnail_dir (), master_name + ".png" )
100+ master_path = os .path .join (self .thumbnail_dir , extract_name (self .filepath ) + ".png" )
109101
110102 for frame , * _ , x , y in self .thumbnails ():
111103 with Image .open (frame ) as image :
@@ -121,8 +113,8 @@ def format_time(secs):
121113 return ("0%s.000" % delta )[:12 ]
122114
123115 metadata = ["WEBVTT\n \n " ]
124- prefix = self .base or os .path .relpath (self .thumbnail_dir () )
125- route = os .path .join (prefix , os . path . basename (self .filepath ))
116+ prefix = self .base or os .path .relpath (self .thumbnail_dir )
117+ route = os .path .join (prefix , extract_name (self .filepath ) + ".png" )
126118
127119 for _ , start , end , x , y in self .thumbnails ():
128120 thumbnail_data = "%s --> %s\n %s#xywh=%d,%d,%d,%d\n \n " % (
@@ -131,40 +123,38 @@ def format_time(secs):
131123 )
132124 metadata .append (thumbnail_data )
133125
134- with open (self .get_metadata_path () , "w" ) as fp :
126+ with open (self .metadata_path , "w" ) as fp :
135127 fp .writelines (metadata )
136128
137129
138130@register_thumbnail ("json" )
139131class ThumbnailJSON (Thumbnail ):
140132 """Implements the methods for generating thumbnails in the JSON format."""
141133
142- def thumbnail_dir (self ):
134+ def calc_thumbnail_dir (self ):
143135 basedir = os .path .abspath (self .output or os .path .dirname (self .filepath ))
144- subdir = os .path .splitext (os .path .basename (self .filepath ))[0 ]
145- basedir = os .path .join (basedir , subdir )
146- create_tree (basedir , [self .filepath ])
147- return basedir
136+ return ensure_tree (os .path .join (basedir , extract_name (self .filepath )), True )
148137
149138 def prepare_frames (self ):
150- thumbnail_dir = self .thumbnail_dir ()
151- if os .path .exists (thumbnail_dir ):
152- remove_tree (thumbnail_dir )
153- copy_tree (self .tempdir .name , thumbnail_dir )
139+ if os .path .exists (self .thumbnail_dir ):
140+ remove_tree (self .thumbnail_dir )
141+ copy_tree (self .tempdir .name , self .thumbnail_dir )
154142 self .tempdir .cleanup ()
155143
156144 def generate (self ):
157145 metadata = {}
158146
159147 for frame , start , * _ in self .thumbnails ():
160- frame = os .path .join (self .thumbnail_dir () , os .path .basename (frame ))
148+ frame = os .path .join (self .thumbnail_dir , os .path .basename (frame ))
161149 with Image .open (frame ) as image :
162150 image .resize ((self .width , self .height ), Image .ANTIALIAS ).save (frame )
151+ prefix = self .base or os .path .relpath (self .thumbnail_dir )
152+ route = os .path .join (prefix , os .path .basename (frame ))
163153 thumbnail_data = {
164- "src" : self . base + frame ,
154+ "src" : route ,
165155 "width" : "%spx" % self .width ,
166156 }
167157 metadata [int (start )] = thumbnail_data
168158
169- with open (self .get_metadata_path () , "w" ) as fp :
159+ with open (self .metadata_path , "w" ) as fp :
170160 json .dump (metadata , fp , indent = 2 )
0 commit comments