diff --git a/README.md b/README.md index cbd87bd..7b7147d 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,31 @@ ## Demo -![alt text](https://git.mosad.xyz/localhorst/codec_visualizer/raw/commit/197fd6273d812ae64f83e629b932a42abc2a4492/demo_output.png "demo output image") +![alt text](https://git.mosad.xyz/localhorst/media_management_scripts/raw/commit/197fd6273d812ae64f83e629b932a42abc2a4492/demo_output.png "demo output image") + + +# check_resolution + +## Usage + +`python ./check_resolution.py path` + +## Features +- find all video files in path +- list following metadata: +- - Name +- - Duration +- - Filesize +- - codec +- - resolution + +## Requirements +- ffmpeg + +## Demo + +TODO + +# convert + +TODO diff --git a/check_Names_and_Paths.sh b/check_Names_and_Paths.sh new file mode 100644 index 0000000..e973428 --- /dev/null +++ b/check_Names_and_Paths.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +##################################################### +# # +# SCRIPT USAGE # +# # +# copy script in root from hdd beside the # +# folderers /movies and /tvshows and run # +# with sh ./check_Names_and_Paths.sh # +# # +# # +##################################################### + +echo "Starting script ..." + +if [[ -d movies ]] +then + echo "Movie directory found" +else + echo "Movie directory not found" + exit 1 +fi + +if [[ -d tvshows ]] +then + echo "TV-Shows directory found" +else + echo "TV-Shows directory not found" + exit 1 +fi + +if [[ -d books ]] +then + echo "Books directory found" +else + echo "Books directory not found" + exit 1 +fi + +echo "" +echo "Checking Movies ..." +echo "" + +for file in movies/* +do + filename=$(basename "$file") # file name with extension + filebasename=$(echo "$filename" | cut -f 1 -d '.') # file name without extension + #echo "Processing $file" + #echo "filename: $filename" + #echo "filebasename: $filebasename" + + ###### check for amount of points ###### + pointCount=$(echo "$filename" | tr -cd '.' | wc -c) + if [[ $pointCount != 1 ]] + then + echo "Incident: Incorrect amount of points: $file" + fi + + ###### check extension ###### + ext="${filename##*.}" + if [ "$ext" != "mkv" ] && [ "$ext" != "mp4" ] + then + echo "Incident: Incorrect extension: $file" + fi + + ###### check for not allowed chars ###### + if [[ ! "$filebasename" =~ ^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0-9\\-\\_]+$ ]] + then + echo "Incident: Not allowed char found in: $file" + fi +done + +#done with movies + +echo "" +echo "Checking TV-Shows ..." +echo "" + +for show in tvshows/*; do + if [[ -d $show ]]; then + showname=$(basename "$show") # file name with extension + #echo "TV-Show found: $show" + #echo "showname: $showname" + + ###### check for not allowed chars in showname ###### + if [[ ! "$showname" =~ ^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0-9\\-\\_]+$ ]] + then + echo "Incident: Not allowed char found in: $show" + fi + + for season in tvshows/$showname/*; do + if [[ -d $season ]]; then + seasonname=$(basename "$season") # file name with extension + #echo "Season found: $season" + #echo "seasonname: $seasonname" + + ###### check for not allowed chars in season name ###### + if [[ ! "$seasonname" =~ ^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0-9\\-\\_]+$ ]] + then + echo "Incident: Not allowed char found in: $season" + fi + + for episode in tvshows/$showname/$seasonname/*; do + if [[ -f $episode ]]; then + episodename=$(basename "$episode") # file name with extension + episodebasename=$(echo "$episodename" | cut -f 1 -d '.') # file name without extension + #echo "Episode found: $episode" + #echo "Episodename: $episodename" + #echo "Episodebasename: $episodebasename" + + ###### check for amount of points ###### + pointCount=$(echo "$episodename" | tr -cd '.' | wc -c) + if [[ $pointCount != 1 ]] + then + echo "Incident: Incorrect amount of points: $episode" + fi + + ###### check extension ###### + ext="${episodename##*.}" + if [ "$ext" != "mkv" ] && [ "$ext" != "mp4" ] + then + echo "Incident: Incorrect extension: $episode" + fi + + ###### check for not allowed chars ###### + if [[ ! "$episodebasename" =~ ^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0-9\\-\\_]+$ ]] + then + echo "Incident: Not allowed char found in: $episode" + fi + fi + done + fi + done + fi +done + +echo "" +echo "Checking Books ..." +echo "" + +for file in books/* +do + filename=$(basename "$file") # file name with extension + filebasename=$(echo "$filename" | cut -f 1 -d '.') # file name without extension + #echo "Processing $file" + #echo "filename: $filename" + #echo "filebasename: $filebasename" + + ###### check for amount of points ###### + pointCount=$(echo "$filename" | tr -cd '.' | wc -c) + if [[ $pointCount != 1 ]] + then + echo "Incident: Incorrect amount of points: $file" + fi + + ###### check extension ###### + ext="${filename##*.}" + if [ "$ext" != "pdf" ] && [ "$ext" != "epub" ] + then + echo "Incident: Incorrect extension: $file" + fi + + ###### check for not allowed chars ###### + if [[ ! "$filebasename" =~ ^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0-9\\-\\_]+$ ]] + then + echo "Incident: Not allowed char found in: $file" + fi +done + +#done with books + +echo "" +echo "Finished script successfully" diff --git a/check_resolution/check_resolution.py b/check_resolution/check_resolution.py new file mode 100644 index 0000000..18c7c98 --- /dev/null +++ b/check_resolution/check_resolution.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" Author: Hendrik Schutter, localhorst@mosad.xyz + Date of creation: 2022/02/13 + Date of last modification: 2022/01/13 +""" + +import os +import sys +import time +import subprocess +import datetime + +def supported_file_extension(filename): + if filename.endswith('.mp4') or filename.endswith('.mkv'): + return True + return False + +def get_length(filename): + result = subprocess.run(["ffprobe", "-v", "error", "-show_entries", + "format=duration", "-of", + "default=noprint_wrappers=1:nokey=1", filename], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + return float(result.stdout) + +def get_codec(filename): + result = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "v:0", + "-show_entries", "stream=codec_name", + "-of", "default=noprint_wrappers=1:nokey=1", filename], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + return str(result.stdout.decode("utf-8")).rstrip("\n") + +def get_resolution(filename): + result = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "v:0", + "-show_entries", "stream=width,height", + "-of", "default=noprint_wrappers=1:nokey=1", filename], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + return str(result.stdout.decode("utf-8")).rstrip("\n").replace("\n", "x") + +def human_readable_size(size, decimal_places=2): + for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']: + if size < 1024.0 or unit == 'PiB': + break + size /= 1024.0 + return f"{size:.{decimal_places}f} {unit}" + +def main() -> None: + if(len(sys.argv) != 2): + path = '.' + else: + path = sys.argv[1] + + files = filter(supported_file_extension, os.listdir(path)) + for file in files: + print ("{:<35} {:<10} {:<5} {:<5} {:<5}".format( file, str(datetime.timedelta(seconds=get_length(file))), human_readable_size(os.path.getsize(file)), get_codec(file), get_resolution(file))) + +if __name__ == "__main__": + main() diff --git a/codec_visualizer/codec_visualizer.py b/codec_visualizer/codec_visualizer.py new file mode 100755 index 0000000..919c3d3 --- /dev/null +++ b/codec_visualizer/codec_visualizer.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" Author: Hendrik Schutter, localhorst@mosad.xyz + Date of creation: 2022/01/22 + Date of last modification: 2022/01/25 +""" + +import os +import sys +import time +import subprocess +import shlex +import shutil +import glob +import PIL +from PIL import Image +from PIL import ImageFont +from PIL import ImageDraw + +temp_dir = os.path.join(os.getcwd(), "_tempCodecVisualizer/") + +def create_temp_dir(): + try: + os.mkdir(temp_dir) + except OSError: + print("Unable to create temp dir: %s" % temp_dir) + sys.exit() + +def delete_temp_dir(): + try: + shutil.rmtree(temp_dir) + except OSError: + pass + +def get_length(filename): + result = subprocess.run(["ffprobe", "-v", "error", "-show_entries", + "format=duration", "-of", + "default=noprint_wrappers=1:nokey=1", filename], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + return float(result.stdout) + +def get_codec(filename): + result = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "v:0", + "-show_entries", "stream=codec_name", + "-of", "default=noprint_wrappers=1:nokey=1", filename], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + return str(result.stdout.decode("utf-8")).rstrip("\n") + +def extract_frame(video_file, time_offset, output_file): + cmd = "ffmpeg -y -ss " + time.strftime('%H:%M:%S', time.gmtime(time_offset)) + ".00 -i " + str(video_file) + " -frames:v 1 " + str(output_file) + devnull = open(os.devnull, 'w') + subprocess.call(shlex.split(cmd),stdout=devnull, stderr=devnull) + +def get_font_path(): + path = "/usr/share/fonts" + files = glob.glob(path + "/**/DejaVuSans-Bold.ttf", recursive = True) + return files[0] + +def zoom_at(img, crop_factor): + image_width, image_height = img.size + img = img.crop((int(image_width/2) - int(image_width/crop_factor) / (crop_factor * 2), int(image_height/2) - int(image_height/crop_factor) / (crop_factor * 2), + int(image_width/2) + int(image_width/crop_factor) / (crop_factor * 2), int(image_height/2) + int(image_height/crop_factor) / (crop_factor * 2))) + return img.resize((int(image_width/crop_factor*2), int(image_height/crop_factor*2)), Image.LANCZOS) + +def create_collage(images_A, images_B, statistics, output_file): + image_width, image_height = images_A[0].size + x_offset = int(image_width/15) + y_offset = int(image_height/1.5) + output_width = image_width *2 + x_offset + output_height = image_height *4 + y_offset + output_image = Image.new('RGB', (output_width, output_height)) + + font_file = get_font_path() + draw = ImageDraw.Draw(output_image) + + text = "codec visualizer" + draw.text((x_offset, 0), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/10))) + + text = "reduced data size: " + str(statistics["compression_rate"]) + "%" + draw.text((x_offset + image_width , int(image_height/18)), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/18))) + + text = str(statistics["codec"][0]) + draw.text((x_offset , int(image_height/18) * 5), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25))) + text = str(statistics["codec"][1]) + draw.text((x_offset + image_width , int(image_height/18) * 5), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25))) + + text = str(statistics["filename"][0]) + draw.text((x_offset , int(image_height/18) * 7), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25))) + text = str(statistics["filename"][1]) + draw.text((x_offset + image_width , int(image_height/18) * 7), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25))) + + text = str(statistics["size"][0]) + draw.text((x_offset , int(image_height/18) * 9), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25))) + text = str(statistics["size"][1]) + draw.text((x_offset + image_width , int(image_height/18) * 9), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25))) + + i = 0 + y = y_offset + for row in range(4): + output_image.paste(images_A[i], (int(x_offset/2), y)) + output_image.paste(images_B[i], (int(x_offset/2) + image_width, y)) + i += 1 + y += image_height + + i = 0 + y = y_offset + crop_factor = 6 + for row in range(4): + cropped_image = zoom_at(images_A[i], crop_factor) + cropped_image_with, _ = cropped_image.size + output_image.paste(cropped_image, (int(x_offset/2) + image_width - cropped_image_with , y)) + cropped_image = zoom_at(images_B[i].transpose(PIL.Image.FLIP_LEFT_RIGHT), crop_factor) + output_image.paste(cropped_image, (int(x_offset/2) + image_width, y)) + i += 1 + y += image_height + + output_image.save(output_file) + +def calc_compression_rate(size_A, size_B): + if size_A == size_B: + return 0 + if size_A > size_B: + return int(100 - 100/size_A*size_B) + if size_B > size_A: + return int(100 - 100/size_B*size_A) + +def human_readable_size(size, decimal_places=2): + for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']: + if size < 1024.0 or unit == 'PiB': + break + size /= 1024.0 + return f"{size:.{decimal_places}f} {unit}" + +def main() -> None: + if(len(sys.argv) != 3): + print("Bad usage!") + print("Usage: python ./codec_analyzer.py file file") + sys.exit() + + file_A = sys.argv[1] + file_B = sys.argv[2] + + if (int(get_length(file_A)) != int(get_length(file_B))): + print("video files have different lengths") + sys.exit() + + video_lenght = int(get_length(file_A)) + + delete_temp_dir() + create_temp_dir() + + stills_timestamps = [] + stills_file_A = [] + stills_file_B = [] + + stills_timestamps.append(int(video_lenght*0.15)) #don't get the intro + stills_timestamps.append(int(video_lenght*0.33)) + stills_timestamps.append(int(video_lenght*0.50)) + stills_timestamps.append(int(video_lenght*0.75)) #don't get the outro + + for frame_timestamp in stills_timestamps: + still_file_A = temp_dir + "A_"+ str(frame_timestamp) + ".tif" + extract_frame(file_A, frame_timestamp, still_file_A) + stills_file_A.append(Image.open(still_file_A)) + + still_file_B = temp_dir + "B_"+ str(frame_timestamp) + ".tif" + extract_frame(file_B, frame_timestamp, still_file_B) + stills_file_B.append(Image.open(still_file_B)) + + file_statistics = { + "filename": [os.path.basename(file_A),os.path.basename(file_B)], + "size": [human_readable_size(os.path.getsize(file_A)), human_readable_size(os.path.getsize(file_B))], + "codec": [get_codec(file_A), get_codec(file_B)], + "compression_rate": calc_compression_rate(os.path.getsize(file_A), os.path.getsize(file_B)) + } + + create_collage(stills_file_A, stills_file_B, file_statistics, "output.png") + + delete_temp_dir() + +if __name__ == "__main__": + main() diff --git a/codec_visualizer/demo_output.png b/codec_visualizer/demo_output.png new file mode 100644 index 0000000..b8411c3 Binary files /dev/null and b/codec_visualizer/demo_output.png differ diff --git a/convert/convert_show.sh b/convert/convert_show.sh new file mode 100755 index 0000000..7de564f --- /dev/null +++ b/convert/convert_show.sh @@ -0,0 +1,37 @@ +#! /bin/sh + +####################################### HOW TO USE ################################################ +# manually copy script in show folder next to the seasons +# manually rename the seasons folders like Season_01, Season_02, ..., Season_42 +# manually rename the episodes with PREFIX old_ like old_ShowName_S01_E01 like: for f in * ; do mv -- "$f" "old_$f" ; done +# run script for testing (uncomment line 28 for conversions) +# manually delete after completion all files with PREFIX old_ +# find . -name "*.nfo" -exec rm -rf {} \; +##################################################################################################### + +PREFIX="old_" +echo "starting script ..." +echo " " +ROOT_DIR=$PWD # get current directory aka. the show folder +echo "ROOT_DIR: " $ROOT_DIR +for seasons in $ROOT_DIR/**; do +if [[ -d $seasons ]]; then + echo "Season found: $seasons" + cd $seasons + for episodes in ${PREFIX}*; do + OLD_EPISODE_NAME=$episodes + NEW_EPISODE_NAME=$"$(b=${episodes##*/}; echo ${b%.*}).mkv" + NEW_EPISODE_NAME=$"${NEW_EPISODE_NAME//$PREFIX/}" + + echo "OLD_EPISODE_NAME: $OLD_EPISODE_NAME" + echo "NEW_EPISODE_NAME: $NEW_EPISODE_NAME" + + if [ -f "$NEW_EPISODE_NAME" ]; then + echo "$NEW_EPISODE_NAME exists." + else + echo "$NEW_EPISODE_NAME does not exist." + #ffmpeg -i $OLD_EPISODE_NAME -c:v libaom-av1 -c:a libopus -mapping_family 1 -af aformat=channel_layouts=5.1 -c:s copy -map 0 -crf 24 -b:v 0 -b:a 128k -cpu-used 4 -row-mt 1 -tiles 2x2 $NEW_EPISODE_NAME + fi + done +fi +done