automatic-time-lapse-creator 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. automatic_time_lapse_creator-0.0.0/LICENSE +19 -0
  2. automatic_time_lapse_creator-0.0.0/PKG-INFO +109 -0
  3. automatic_time_lapse_creator-0.0.0/README.md +74 -0
  4. automatic_time_lapse_creator-0.0.0/pyproject.toml +47 -0
  5. automatic_time_lapse_creator-0.0.0/setup.cfg +4 -0
  6. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/__init__.py +0 -0
  7. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/cache_manager.py +19 -0
  8. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/common/constants.py +15 -0
  9. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/common/exceptions.py +13 -0
  10. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/common/utils.py +13 -0
  11. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/source.py +69 -0
  12. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/time_lapse_creator.py +419 -0
  13. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/time_manager.py +98 -0
  14. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator/video_manager.py +100 -0
  15. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator.egg-info/PKG-INFO +109 -0
  16. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator.egg-info/SOURCES.txt +24 -0
  17. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator.egg-info/dependency_links.txt +1 -0
  18. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator.egg-info/requires.txt +14 -0
  19. automatic_time_lapse_creator-0.0.0/src/automatic_time_lapse_creator.egg-info/top_level.txt +1 -0
  20. automatic_time_lapse_creator-0.0.0/tests/test_cache_manager.py +49 -0
  21. automatic_time_lapse_creator-0.0.0/tests/test_data.py +19 -0
  22. automatic_time_lapse_creator-0.0.0/tests/test_mocks.py +45 -0
  23. automatic_time_lapse_creator-0.0.0/tests/test_source.py +92 -0
  24. automatic_time_lapse_creator-0.0.0/tests/test_time_lapse_creator.py +612 -0
  25. automatic_time_lapse_creator-0.0.0/tests/test_time_manager.py +102 -0
  26. automatic_time_lapse_creator-0.0.0/tests/test_video_manager.py +80 -0
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.1
2
+ Name: automatic_time_lapse_creator
3
+ Version: 0.0.0
4
+ Summary: automatic_time_lapse_creator is a Python program for scraping images from a web cam url and converting them into a timelapse
5
+ Author-email: Kaloyan Peychev <kokoeverest@gmail.com>
6
+ Maintainer-email: Kaloyan Peychev <kokoeverest@gmail.com>
7
+ Project-URL: Repository, https://github.com/kokoeverest/Automatic-time-lapse-creator
8
+ Project-URL: Issues, https://github.com/kokoeverest/Automatic-time-lapse-creator/issues
9
+ Keywords: requests,astral,opencv,opencv-python,timelapse,video,mp4,webcam
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: astral>=3.2
22
+ Requires-Dist: certifi>=2024.8.30
23
+ Requires-Dist: charset-normalizer>=3.4.0
24
+ Requires-Dist: colorama>=0.4.6
25
+ Requires-Dist: idna>=3.10
26
+ Requires-Dist: iniconfig>=2.0.0
27
+ Requires-Dist: numpy>=2.1.3
28
+ Requires-Dist: opencv-python>=4.10.0.84
29
+ Requires-Dist: packaging>=24.2
30
+ Requires-Dist: pluggy>=1.5.0
31
+ Requires-Dist: pytest>=8.3.3
32
+ Requires-Dist: requests>=2.32.3
33
+ Requires-Dist: tzdata>=2024.2
34
+ Requires-Dist: urllib3>=2.2.3
35
+
36
+ # Automatic time lapse creator
37
+
38
+ ### A Python package for extracting images from a web cam url and converting these images into a timelapse. The process is intended to be automatic, so the only parameters that need to be provided are:
39
+
40
+ + the image resourse url/urls pointing to an image *(not video!)*
41
+ + the path on your computer where the images will be stored *(default is os.getcwd())*
42
+ + the location of the city (or coordinates) for which the daylight will be calculated *(default is Sofia, Bulgaria)*
43
+
44
+ ### The purpose of the program is to get an archive/history of the weather in any place that has accessible web cam, so people can actually see what the real weather was like in this place and compare it with weather forecast and/or data from a weather station.
45
+
46
+ ### Libraries used in the program:
47
+ + Astral (https://astral.readthedocs.io/en/latest/) - to get the
48
+ sunrise and sunset time of the day for a specific geolocation
49
+ + OpenCV-Python (https://pypi.org/project/opencv-python/) - to
50
+ read/resize the jpeg files and build a time lapse mp4 video
51
+ + Requests - builtin module used to retrieve the image from the url
52
+ + Logging - the builtin python looging tool for creating comprehensive
53
+ logs of the program execution
54
+
55
+ ### Main flow and automation of the app:
56
+ #### When the execution of the TimeLapseCreator object starts, it will check if it's daylight at the provided location. Daylight is calculated automatically using the Astral library so there will be few or no images collected during the night. After the collection of images finishes for the day the VideoManager creates a video from the collected images for each of the provided sources and deletes all the images for the day. In case of an interruption of the collection of images during the day (for example: power outage -> the program stops and then it's started again), the video will still be created but the daily images won't be deleted. In this case you can inspect them and create a video manually from the pictures that are worth it.
57
+ #### During the night the program will not collect any images - they will be collected when there is daylight - the smart power of the Astral library ;)
58
+
59
+ #### A valid scenario for creating a TimeLapseCreator for webcams in Bulgaria:
60
+ *Note that no location is provided in this example, so the TimeLapseCreator will be instantiated for the default location: Sofia, Bulgaria.*
61
+ ```python
62
+ from automatic_time_lapse_creator.time_lapse_creator import TimeLapseCreator
63
+ from automatic_time_lapse_creator.source import Source
64
+
65
+ # Valid sources
66
+ borovets_source = Source(
67
+ "markudjik", "https://media.borovets-bg.com/cams/channel?channel=31"
68
+ )
69
+ pleven_hut_source = Source(
70
+ "plevenhut", "https://meter.ac/gs/nodes/N160/snap.jpg?1705436803718"
71
+ )
72
+
73
+ sources_list: list[Source] = [borovets_source, pleven_hut_source]
74
+
75
+ # creating a TimeLapseCreator can be done in two ways:
76
+ # instantiate the creator directly with the list of Sources
77
+ bulgaria_webcams = TimeLapseCreator(sources_list)
78
+
79
+ # or create a new TimeLapseCreator and add the list of Sources
80
+ # with the add_sources method
81
+ bulgaria_webcams = TimeLapseCreator()
82
+ bulgaria_webcams.add_sources(sources_list)
83
+
84
+ # if you try to instantiate a new TimeLapseCreator with a single Source, it will raise an InvalidCollectionException
85
+ # for example:
86
+ pleven_hut_webcam = TimeLapseCreator(pleven_hut_source)
87
+ # output:
88
+ Traceback...:
89
+ ...in validate_collection
90
+ raise InvalidCollectionException(
91
+ src.automatic_time_lapse_creator.common.exceptions.InvalidCollectionException: Only list, tuple or set collections are allowed!
92
+
93
+ # start the collection of the images with the execute() method
94
+ bulgaria_webcams.execute()
95
+ ```
96
+
97
+ An invalid scenario will be:
98
+ ```python
99
+ # Invalid source
100
+ sample_source_with_empty_url = Source("fake", "https://empty.url")
101
+
102
+ invalid_source_list = [sample_source_with_empty_url]
103
+
104
+ invalid_url_creator = TimeLapseCreator(invalid_source_list)
105
+
106
+ invalid_url_creator.execute()
107
+ ```
108
+
109
+ Should you have any questions, bug reports or recommendations, feel free to send an email to *kokoeverest[@]gmail.com*
@@ -0,0 +1,74 @@
1
+ # Automatic time lapse creator
2
+
3
+ ### A Python package for extracting images from a web cam url and converting these images into a timelapse. The process is intended to be automatic, so the only parameters that need to be provided are:
4
+
5
+ + the image resourse url/urls pointing to an image *(not video!)*
6
+ + the path on your computer where the images will be stored *(default is os.getcwd())*
7
+ + the location of the city (or coordinates) for which the daylight will be calculated *(default is Sofia, Bulgaria)*
8
+
9
+ ### The purpose of the program is to get an archive/history of the weather in any place that has accessible web cam, so people can actually see what the real weather was like in this place and compare it with weather forecast and/or data from a weather station.
10
+
11
+ ### Libraries used in the program:
12
+ + Astral (https://astral.readthedocs.io/en/latest/) - to get the
13
+ sunrise and sunset time of the day for a specific geolocation
14
+ + OpenCV-Python (https://pypi.org/project/opencv-python/) - to
15
+ read/resize the jpeg files and build a time lapse mp4 video
16
+ + Requests - builtin module used to retrieve the image from the url
17
+ + Logging - the builtin python looging tool for creating comprehensive
18
+ logs of the program execution
19
+
20
+ ### Main flow and automation of the app:
21
+ #### When the execution of the TimeLapseCreator object starts, it will check if it's daylight at the provided location. Daylight is calculated automatically using the Astral library so there will be few or no images collected during the night. After the collection of images finishes for the day the VideoManager creates a video from the collected images for each of the provided sources and deletes all the images for the day. In case of an interruption of the collection of images during the day (for example: power outage -> the program stops and then it's started again), the video will still be created but the daily images won't be deleted. In this case you can inspect them and create a video manually from the pictures that are worth it.
22
+ #### During the night the program will not collect any images - they will be collected when there is daylight - the smart power of the Astral library ;)
23
+
24
+ #### A valid scenario for creating a TimeLapseCreator for webcams in Bulgaria:
25
+ *Note that no location is provided in this example, so the TimeLapseCreator will be instantiated for the default location: Sofia, Bulgaria.*
26
+ ```python
27
+ from automatic_time_lapse_creator.time_lapse_creator import TimeLapseCreator
28
+ from automatic_time_lapse_creator.source import Source
29
+
30
+ # Valid sources
31
+ borovets_source = Source(
32
+ "markudjik", "https://media.borovets-bg.com/cams/channel?channel=31"
33
+ )
34
+ pleven_hut_source = Source(
35
+ "plevenhut", "https://meter.ac/gs/nodes/N160/snap.jpg?1705436803718"
36
+ )
37
+
38
+ sources_list: list[Source] = [borovets_source, pleven_hut_source]
39
+
40
+ # creating a TimeLapseCreator can be done in two ways:
41
+ # instantiate the creator directly with the list of Sources
42
+ bulgaria_webcams = TimeLapseCreator(sources_list)
43
+
44
+ # or create a new TimeLapseCreator and add the list of Sources
45
+ # with the add_sources method
46
+ bulgaria_webcams = TimeLapseCreator()
47
+ bulgaria_webcams.add_sources(sources_list)
48
+
49
+ # if you try to instantiate a new TimeLapseCreator with a single Source, it will raise an InvalidCollectionException
50
+ # for example:
51
+ pleven_hut_webcam = TimeLapseCreator(pleven_hut_source)
52
+ # output:
53
+ Traceback...:
54
+ ...in validate_collection
55
+ raise InvalidCollectionException(
56
+ src.automatic_time_lapse_creator.common.exceptions.InvalidCollectionException: Only list, tuple or set collections are allowed!
57
+
58
+ # start the collection of the images with the execute() method
59
+ bulgaria_webcams.execute()
60
+ ```
61
+
62
+ An invalid scenario will be:
63
+ ```python
64
+ # Invalid source
65
+ sample_source_with_empty_url = Source("fake", "https://empty.url")
66
+
67
+ invalid_source_list = [sample_source_with_empty_url]
68
+
69
+ invalid_url_creator = TimeLapseCreator(invalid_source_list)
70
+
71
+ invalid_url_creator.execute()
72
+ ```
73
+
74
+ Should you have any questions, bug reports or recommendations, feel free to send an email to *kokoeverest[@]gmail.com*
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "automatic_time_lapse_creator"
7
+ authors = [
8
+ { name="Kaloyan Peychev", email="kokoeverest@gmail.com" },
9
+ ]
10
+ maintainers = [
11
+ { name="Kaloyan Peychev", email="kokoeverest@gmail.com" },
12
+ ]
13
+ classifiers = [
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ ]
23
+ dependencies = [
24
+ "astral>=3.2",
25
+ "certifi>=2024.8.30",
26
+ "charset-normalizer>=3.4.0",
27
+ "colorama>=0.4.6",
28
+ "idna>=3.10",
29
+ "iniconfig>=2.0.0",
30
+ "numpy>=2.1.3",
31
+ "opencv-python>=4.10.0.84",
32
+ "packaging>=24.2",
33
+ "pluggy>=1.5.0",
34
+ "pytest>=8.3.3",
35
+ "requests>=2.32.3",
36
+ "tzdata>=2024.2",
37
+ "urllib3>=2.2.3",
38
+ ]
39
+ description = "automatic_time_lapse_creator is a Python program for scraping images from a web cam url and converting them into a timelapse"
40
+ dynamic = ["version"]
41
+ keywords = ["requests", "astral", "opencv", "opencv-python", "timelapse", "video", "mp4", "webcam"]
42
+ readme = "README.md"
43
+ requires-python = ">=3.10"
44
+
45
+ [project.urls]
46
+ Repository = "https://github.com/kokoeverest/Automatic-time-lapse-creator"
47
+ Issues = "https://github.com/kokoeverest/Automatic-time-lapse-creator/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,19 @@
1
+ import pickle
2
+
3
+
4
+ class CacheManager:
5
+ """Class for managing the state of TimeLapseCreator objects. State of the object
6
+ is saved (pickled) in a file and the filename has a prefix *cache_* and ends with
7
+ the *location_name* attribute of the TimeLapseCreator"""
8
+
9
+ @classmethod
10
+ def write(cls, time_lapse_creator: object, location: str):
11
+ """Writes the TimeLapseCreator object to a file, overwriting existing objects
12
+ if the file already exists"""
13
+ pickle.dump(time_lapse_creator, open(f"cache_{location}.p", "wb"))
14
+
15
+ @classmethod
16
+ def get(cls, location: str):
17
+ """Retrieves the pickled object in the file. If the file is empty or if it is not found
18
+ it will return an Exception"""
19
+ return pickle.load(open(f"cache_{location}.p", "rb"))
@@ -0,0 +1,15 @@
1
+ JPG_FILE = ".jpg"
2
+ MP4_FILE = ".mp4"
3
+ LOG_FILE = ".log"
4
+ YYMMDD_FORMAT = "%Y-%m-%d"
5
+ HHMMSS_UNDERSCORE_FORMAT = "%H_%M_%S"
6
+ HHMMSS_COLON_FORMAT = "%H:%M:%S %p"
7
+ OK_STATUS_CODE = 200
8
+ NO_CONTENT_STATUS_CODE = 204
9
+ LOGGING_FORMAT = "%(name)s: %(asctime)s - %(levelname)s - %(message)s"
10
+ DEFAULT_CITY_NAME = "Sofia"
11
+ DEFAULT_SECONDS_BETWEEN_FRAMES = 60
12
+ DEFAULT_NIGHTTIME_RETRY_SECONDS = 300
13
+ DEFAULT_VIDEO_FPS = 30
14
+ DEFAULT_VIDEO_WIDTH = 640
15
+ DEFAULT_VIDEO_HEIGHT = 360
@@ -0,0 +1,13 @@
1
+ class UnknownLocationException(Exception):
2
+ def __init__(self, *args: object) -> None:
3
+ super().__init__(*args)
4
+
5
+
6
+ class InvalidStatusCodeException(Exception):
7
+ def __init__(self, *args: object) -> None:
8
+ super().__init__(*args)
9
+
10
+
11
+ class InvalidCollectionException(Exception):
12
+ def __init__(self, *args: object) -> None:
13
+ super().__init__(*args)
@@ -0,0 +1,13 @@
1
+ def create_log_message(location: str, url: str, method: str):
2
+ """
3
+ Creates an appropriate log message according to the method which calls it
4
+
5
+ Returns::
6
+
7
+ str - the log message if the method is 'add' or 'remove'"""
8
+ if method == "add":
9
+ return f"Source with location: {location} or url: {url} already exists!"
10
+ elif method == "remove":
11
+ return f"Source with location: {location} or url: {url} doesn't exist!"
12
+ else:
13
+ return f"Unknown command: {method}"
@@ -0,0 +1,69 @@
1
+ class Source:
2
+ """Contains two public attributes and four read-only attributes, which can be changed
3
+ through the respective methods.
4
+
5
+ Attributes::
6
+
7
+ location_name: str - a folder with that name will be created on your pc. The videos
8
+ for every day of the execution of the TimeLapseCreator will be created and put into
9
+ subfolders into the "location_name" folder
10
+
11
+ url: str - a valid web address where a webcam frame (image) should be located.
12
+
13
+ ##### Be sure that the url does not point to a video resource."""
14
+
15
+ def __init__(self, location_name: str, url: str) -> None:
16
+ self.location_name: str = location_name
17
+ self.url: str = url
18
+ self._video_created: bool = False
19
+ self._images_count: int = 0
20
+ self._all_images_collected: bool = False
21
+ self._images_partially_collected: bool = False
22
+
23
+ @property
24
+ def images_collected(self):
25
+ return self._all_images_collected
26
+
27
+ @property
28
+ def images_partially_collected(self):
29
+ return self._images_partially_collected
30
+
31
+ @property
32
+ def images_count(self):
33
+ return self._images_count
34
+
35
+ @property
36
+ def video_created(self):
37
+ return self._video_created
38
+
39
+ def set_video_created(self):
40
+ """Set the video_created to True"""
41
+ self._video_created = True
42
+
43
+ def reset_video_created(self):
44
+ """Reset the video_created to False"""
45
+ self._video_created = False
46
+
47
+ def increase_images(self):
48
+ """Increases the count of the images by 1"""
49
+ self._images_count += 1
50
+
51
+ def reset_images_counter(self):
52
+ """Resets the images count to 0"""
53
+ self._images_count = 0
54
+
55
+ def set_all_images_collected(self):
56
+ """Sets the self._all_images_collected to True"""
57
+ self._all_images_collected = True
58
+
59
+ def set_images_partially_collected(self):
60
+ """Sets the self._images_partially_collected to True"""
61
+ self._images_partially_collected = True
62
+
63
+ def reset_all_images_collected(self):
64
+ """Resets the self._all_images_collected to False"""
65
+ self._all_images_collected = False
66
+
67
+ def reset_images_pertially_collected(self):
68
+ """Resets the self._images_partially_collected to False"""
69
+ self._images_partially_collected = False