commit 6a5d8e66e75ef9f02bd92033ec759e903038cc2c Author: Kamal Curi Date: Sat Jun 15 16:01:36 2024 -0300 ADD: Repository initialization diff --git a/README.md b/README.md new file mode 100644 index 0000000..e15aa56 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# NOAA GOES-East (GOES-16) archive downloader + +This script is known to work with Python `3.12` +This script requires `curl` to work, and `ffmpeg` to animate the downloaded images + + +For information, run: + +``` +$ python downloader.py -h +usage: downloader.py [-h] [--directory DIRECTORY] [--size SIZE] [--animate ANIMATE] [--framerate FRAMERATE] [--compress-size COMPRESS_SIZE] [--codec CODEC] day + +positional arguments: + day Day of the year. From 1 to 366 + +options: + -h, --help show this help message and exit + --directory DIRECTORY + Output directory for the images + --size SIZE Image size to download, defaults to 1808x1808. Available options: 339x339, 678x678, 1808x1808, 5424x5424, 10848x10848, 21696x21696 + --animate ANIMATE Animate the download images. Available options: fullsize, compressed, ad-fullsize, ad-compressed + --framerate FRAMERATE + Framerate of the animation from the downloaded images, in frames per second, defaults to 8 + --compress-size COMPRESS_SIZE + Resolution of the compressed video, defaults to 540x540 + --codec CODEC Defines the ffmpeg codec to be used. Defaults to libx264. To see available codecs for ffmpeg run "ffmpeg -codecs" +``` diff --git a/downloader.py b/downloader.py new file mode 100644 index 0000000..1ed0948 --- /dev/null +++ b/downloader.py @@ -0,0 +1,145 @@ +import subprocess +import argparse +from os import path, makedirs + + +parser = argparse.ArgumentParser() +parser.add_argument("day", help="Day of the year. From 1 to 366") +parser.add_argument("--directory", help="Output directory for the images") +parser.add_argument("--size", help="Image size to download, defaults to 1808x1808. Available options: 339x339, 678x678, 1808x1808, 5424x5424, 10848x10848, 21696x21696") +parser.add_argument("--animate", help="Animate the download images. Available options: fullsize, compressed, ad-fullsize, ad-compressed") +parser.add_argument("--framerate", help="Framerate of the animation from the downloaded images, in frames per second, defaults to 8") +parser.add_argument("--compress-size", help="Resolution of the compressed video, defaults to 540x540") +parser.add_argument("--codec", help="Defines the ffmpeg codec to be used. Defaults to libx264. To see available codecs for ffmpeg run \"ffmpeg -codecs\"") +args = parser.parse_args() +day = args.day +animateAction = args.animate + + +# Argument handling +directory= "." +if args.directory is not None: + directory = args.directory +if not path.isdir(directory): + makedirs(directory) + +picsize = "1808x1808" +if args.size is not None: + picsize = args.size + +framerate = 8 +if args.framerate is not None: + if animateAction is not None: + framerate = args.framerate + else: + print("'--framerate' requires '--animate' to be set, see '-h' for help and details.") + exit() + + +compress_size= "540x540" +if args.compress_size is not None: + if animateAction is not None: + compress_size = args.compress_size + else: + print("'--compress-size' requires '--animate' to be set, see '-h' for help and details.") + exit() + +codec = "libx264" +if args.codec is not None: + if animateAction is not None: + codec = args.codec + else: + print("'--codec' requires '--animate' to be set, see '-h' for help and details.") + exit() + +# Global counters +pic_count = 1 +total_expected_pics = 144 + +def padding(count: int): + global total_expected_pics + digits = len(str(count)) + count = str(count) + padded_string = "" + while len(count) < len(str(total_expected_pics)): + count = "0"+count + return count + +def daygroup(day: str): + global total_expected_pics + if day.__contains__("-"): + ranges = day.split("-") + total_expected_pics = 144*len(range(int(ranges[0]), int(ranges[1])+1)) + print(f"EXPECTED NUMBER OF IMAGES: {total_expected_pics}") + for d in range(int(ranges[0]), int(ranges[1])+1): + hourgroup(d) + else: + hourgroup(int(day)) + +def hourgroup(day: int): + for hour in range(0, 24): + if hour < 10: + hour = "0" + str(hour) + minutegroup(day, hour) + +def minutegroup(day: int, hour: str): + global pic_count + global directory + global animateAction + global picsize + str_pic_count = "" + for minutes in range(0, 60, 10): + if minutes == 0: + minutes = "00" + minutes = str(minutes) + day = str(day) + str_pic_count = padding(pic_count) + fulldir = directory+"/"+str_pic_count+".jpg" + print(f"DOWNLOADING PICNUM={str_pic_count}, DAY={day}, HOUR={hour}, MINUTES={minutes}") + print(f"LINK: https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/GEOCOLOR/2024{day}{hour}{minutes}_GOES16-ABI-FD-GEOCOLOR-{picsize}.jpg") + subprocess.run(["sh", "-c", f"curl -o {fulldir} https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/GEOCOLOR/2024{day}{hour}{minutes}_GOES16-ABI-FD-GEOCOLOR-{picsize}.jpg &>/dev/null"]) + print("CHECKING FILE") + filetest = subprocess.run(["file", fulldir], capture_output=True) + filetest = filetest.stdout.decode("utf-8") + if filetest.__contains__("JPEG"): + print(f"FILE {fulldir} OK") + else: + print(f"FILE {fulldir} NOT OK, DELETING") + subprocess.run(["rm", fulldir]) + pic_count += 1 + +if animateAction is not None: + if animateAction == "ad-fullsize": + print(f"CREATING FULL-SIZED ANIMATION {directory}/fullsize.mp4") + subprocess.run(["sh", "-c", f"ffmpeg -framerate {framerate} -pattern_type glob -i '{directory}/*.jpg' -c:v {codec} {directory}/fullsize.mp4"]) + exit() + elif animateAction == "ad-compressed": + print(f"CREATING 540x540 COMPRESSED ANIMATION {directory}/compressed.mp4") + subprocess.run(["sh", "-c", f"ffmpeg -framerate {framerate} -pattern_type glob -i '{directory}/*.jpg' -c:v {codec} -crf 25 -s {compress_size} {directory}/compressed.mp4"]) + exit() + elif animateAction == "ad-fullsized+compressed": + print(f"CREATING FULL-SIZED ANIMATION {directory}/fullsize.mp4") + subprocess.run(["sh", "-c", f"ffmpeg -framerate {framerate} -pattern_type glob -i '{directory}/*.jpg' -c:v {codec} {directory}/fullsize.mp4"]) + print(f"CREATING 540x540 COMPRESSED VERSION FROM FULL-SIZED ANIMATION") + subprocess.run(["sh", "-c", f"ffmpeg -i {directory}/fullsize.mp4 -crf 25 -s {compress_size} {directory}/compressed.mp4"]) + exit() + +daygroup(day) + +if animateAction is not None: + if animateAction == "fullsize": + print(f"CREATING FULL-SIZED ANIMATION {directory}/fullsize.mp4") + subprocess.run(["sh", "-c", f"ffmpeg -framerate {framerate} -pattern_type glob -i '{directory}/*.jpg' -c:v {codec} {directory}/fullsize.mp4"]) + exit() + elif animateAction == "compressed": + print(f"CREATING 540x540 COMPRESSED ANIMATION {directory}/compressed.mp4") + subprocess.run(["sh", "-c", f"ffmpeg -framerate {framerate} -pattern_type glob -i '{directory}/*.jpg' -c:v {codec} -crf 25 -s {compress_size} {directory}/compressed.mp4"]) + exit() + elif animateAction == "fullsized+compressed": + print(f"CREATING FULL-SIZED ANIMATION {directory}/fullsize.mp4") + subprocess.run(["sh", "-c", f"ffmpeg -framerate {framerate} -pattern_type glob -i '{directory}/*.jpg' -c:v {codec} {directory}/fullsize.mp4"]) + print(f"CREATING 540x540 COMPRESSED VERSION FROM FULL-SIZED ANIMATION") + subprocess.run(["sh", "-c", f"ffmpeg -i {directory}/fullsize.mp4 -crf 25 -s {compress_size} {directory}/compressed.mp4"]) + exit() + +exit() \ No newline at end of file