├── .gitignore ├── LICENSE ├── Music-Video-Generator.zip ├── README.md ├── folderRenderer.py ├── image.PNG └── renderer.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.mp4 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 JPBotelho 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Music-Video-Generator.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JPBotelho/Music-Video-Generator/4650f7b19fbcaffbc72e6f91408a737c3b1827f6/Music-Video-Generator.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Music-Video-Generator 2 | Python / FFMPEG script to generate "topic" videos with album cover and metadata from either a single mp3/flac track or an album directory. 3 | 4 | ## Features 5 | Generate video for a single track with artist name, track name, album name and year. 6 | 7 | Generate video for an album directory with artist name, album name and year. 8 | 9 | Output video is in 1280\*720 with the cover art being scaled to 600\*600. To alter this resolution you will need to alter the placement of the texts and the image pivot. 10 | 11 | # Example (Album) 12 | ![alt text](image.PNG "Logo Title Text 1") 13 | 14 | https://streamable.com/pb3kj 15 | 16 | # Requirements 17 | - FFMPEG 18 | 19 | For single track: 20 | 21 | - .flac / .mp3 file with embedded cover image and the following tags: "artist", "title", "album", "year" or "date" 22 | 23 | For directory: 24 | - Folder with .flac / .mp3 files. First track must have embedded cover image and the above tags, except for "title". 25 | 26 | # Command breakdown 27 | Since this is a massive clusterfuck, here's the example of a generated ffmpeg command for the directory/album generator: 28 | 29 | ``` 30 | ffmpeg -framerate 1 31 | 32 | -i "D:/Programas/FFMPEG/background.jpg" //IMPORT BACKGROUND IMAGE INTO STREAM 0 (1280*720) 33 | 34 | -i "D:\Downloads\Torrents\Music\C.O.I.N.S. - 2018 - Ancient Coins (320kbps)\01 C.O.I.N.S. - Intro.mp3" 35 | 36 | (... inputs) 37 | 38 | -i "D:\Downloads\Torrents\Music\C.O.I.N.S. - 2018 - Ancient Coins (320kbps)\11 C.O.I.N.S. - Ahhh!!! Feat. Makeba Mooncycle.mp3" 39 | 40 | -filter_complex 41 | "[1:v]scale=600:-1[ovrl]; //SCALE COVER ART TO 600*600 42 | 43 | [0:V]drawtext=fontsize=25:fontfile="D:/Programas/FFMPEG/font.ttf":fontcolor=white:text={artist}:x=W/2+W/10.5:y=H/11[f1], //DRAW TEXT OVER THE BACKGROUND STREAM 44 | 45 | [f1]drawtext=fontsize=25:fontfile="D:/Programas/FFMPEG/font.ttf":fontcolor=white:text={albumName}:x=W/2+W/10.5:y=2*(H/10)[f2], //DRAW TEXT OVER THE BACKGROUND STREAM 46 | 47 | [f2]drawtext=fontsize=25:fontfile="D:/Programas/FFMPEG/font.ttf":fontcolor=white:text={year}:x=W/2+W/10.5:y=3*(H/10)[bg]; //DRAW TEXT OVER THE BACKGROUND STREAM 48 | 49 | [bg][ovrl]overlay=(W-w)/7:(H-h)/2; //OVERLAY BACKGROUND (W/ TEXT) WITH COVER IMAGE 50 | 51 | [1:a][2:a][3:a][4:a][5:a][6:a][7:a][8:a][9:a][10:a][11:a]concat=n=11:v=0:a=1" //STITCH AUDIO STREAMS TOGETHER 52 | 53 | -c:v libx264 -r 30 -movflags +faststart "albumVideo.mp4" 54 | 55 | ``` 56 | 57 | 58 | -------------------------------------------------------------------------------- /folderRenderer.py: -------------------------------------------------------------------------------- 1 | from mutagen.flac import FLAC 2 | from mutagen.easyid3 import EasyID3 3 | from mutagen.mp3 import MP3 4 | import os 5 | import os.path 6 | from pathlib import Path 7 | 8 | # ffmpeg -framerate 1 -i "background.jpg" -i test.flac -i pain.flac -i gold.flac -filter_complex "[1:v]scale=600:-1[ovrl]; [0:v][ovrl]overlay=(W-w)/7:(H-h)/2; [1:a][2:a][3:a]concat=n=3:v=0:a=1" -c:v libx264 -r 30 -movflags +faststart "newout.mp4" 9 | #'ffmpeg -framerate 1 -i "background.jpg" -i "D:\\Programas\\FFMPEG\\Album\\01 - Smoking.flac" -i "D:\\Programas\\FFMPEG\\Album\\02 - 1st Thing First.flac" -i "D:\\Programas\\FFMPEG\\Album\\03 - Two Gats Up.flac" -i "D:\\Programas\\FFMPEG\\Album\\04 - Blow Up.flac" -i "D:\\Programas\\FFMPEG\\Album\\05 - Party People.flac" -i "D:\\Programas\\FFMPEG\\Album\\06 - If You Only Knew.flac" -i "D:\\Programas\\FFMPEG\\Album\\07 - Hit Me With That Shit.flac" -i "D:\\Programas\\FFMPEG\\Album\\08 - Hip Hop.flac" -i "D:\\Programas\\FFMPEG\\Album\\09 - Chamber Danger.flac" -i "D:\\Programas\\FFMPEG\\Album\\10 - Underground Emperor.flac" -i "D:\\Programas\\FFMPEG\\Album\\11 - Life Bid.flac" -i "D:\\Programas\\FFMPEG\\Album\\12 - Don't Go Against the Grain.flac" -i "D:\\Programas\\FFMPEG\\Album\\13 - Things Ain't What They Used To Be.flac" -i "D:\\Programas\\FFMPEG\\Album\\14 - Black On Black Crime.flac" -filter_complex "[1:v]scale=600:-1[ovrl]; [0:v][ovrl]overlay=(W-w)/7:(H-h)/2; [1:a][2:a][3:a][4:a][5:a][6:a][7:a][8:a][9:a][10:a][11:a][12:a][13:a][14:a]concat=n=14:v=0:a=1" -c:v libx264 -r 30 -movflags +faststart "albumVideo.mp4"' 10 | def main(): 11 | videoOutput = "BlackMarketMilitia.mp4" 12 | path = "D:/Downloads/Music/Collection/Black Market Militia/Black Market Militia (2005) - Black Market Militia" 13 | backgroundPath = "D:/Programas/FFMPEG/background.jpg" 14 | font = "\'D:/Programas/FFMPEG/font.ttf\'" 15 | fontsize = 25 16 | 17 | q = Path(path) 18 | if not(q.exists()): 19 | raise Exception("Directory does not exist.") 20 | if not(q.is_dir()): 21 | raise Exception("String is not valid directory.") 22 | 23 | files = list(q.glob('*.flac')) 24 | 25 | if (len(files)==0): 26 | files = list(q.glob("*.mp3")) 27 | if(len(files)==0): 28 | raise Exception("Directory has no .flac or .mp3 files.") 29 | else: 30 | metadata = EasyID3(files[0]) 31 | else: 32 | metadata = FLAC(files[0]) 33 | 34 | try: 35 | album = metadata["album"][0] 36 | except: 37 | raise Exception("File #0 does not have \'album\' tag") 38 | # Remove invalid characters (commas and single-quotes) 39 | album = album.replace("\'", "") 40 | album = album.replace(",", "") 41 | 42 | try: 43 | artist = metadata["artist"][0] 44 | except: 45 | raise Exception("File #0 does not have \'artist\' tag") 46 | 47 | try: 48 | date = metadata["date"][0] 49 | except: 50 | try: 51 | date = metadata["year"][0] 52 | except: 53 | raise Exception("File #0 does not have \'date\' nor \'year\' tags") 54 | 55 | #album = album.replace("\"", "") 56 | length = 0 57 | 58 | try: 59 | for file in files: 60 | length += FLAC(file).info.length 61 | except: 62 | for file in files: 63 | length += MP3(file).info.length 64 | 65 | command = f"ffmpeg -loop 1 -framerate 1 -i \"{backgroundPath}\" " 66 | for flacFile in files: 67 | command += (f"-i \"{flacFile}\" ") 68 | 69 | 70 | command += f"-filter_complex \"[1:v]scale=600:-1[ovrl]; [0:V]drawtext=fontsize=25:fontfile=\"D:/Programas/FFMPEG/font.ttf\":fontcolor=white:text={artist}:x=W/2+W/10.5:y=H/11[f1], [f1]drawtext=fontsize=25:fontfile=\"D:/Programas/FFMPEG/font.ttf\":fontcolor=white:text={album}:x=W/2+W/10.5:y=2*(H/10)[f2],[f2]drawtext=fontsize=25:fontfile=\"D:/Programas/FFMPEG/font.ttf\":fontcolor=white:text={date}:x=W/2+W/10.5:y=3*(H/10)[bg]; [bg][ovrl]overlay=(W-w)/7:(H-h)/2; " 71 | for i in range(len(files)): 72 | z = i+1 73 | command += f"[{z}:a]" 74 | fileCount = len(files) 75 | command += f"concat=n={fileCount}:v=0:a=1[final]\" -map 0:v -map [final]:a -t {length} -r 1 -movflags +faststart \"{videoOutput}\"" 76 | print(command) 77 | os.system(command) 78 | 79 | if __name__ == '__main__': 80 | main() -------------------------------------------------------------------------------- /image.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JPBotelho/Music-Video-Generator/4650f7b19fbcaffbc72e6f91408a737c3b1827f6/image.PNG -------------------------------------------------------------------------------- /renderer.py: -------------------------------------------------------------------------------- 1 | from mutagen.flac import FLAC 2 | from mutagen.easyid3 import EasyID3 3 | import os 4 | import os.path 5 | 6 | title = "None" 7 | artist = "None" 8 | date = "None" 9 | album = "None" 10 | def main(): 11 | src = "D:/Programas/FFMPEG/bad.mp3" 12 | output = "D:/Programas/FFMPEG/output.mp4" 13 | bg = "D:/Programas/FFMPEG/background.jpg" 14 | font = "\'D:/Programas/FFMPEG/font.ttf\'" 15 | fontsize = 25 16 | GetTags(src) 17 | 18 | command = f"ffmpeg -framerate 1 -i \"{bg}\" -i \"{src}\" -filter_complex \"[1:v]scale=600:-1, pad=(16/15)*iw:(16/15)*ih:(ow-iw)/2:(oh-ih)/2:white, [0:v]overlay=(W-w)/7:(H-h)/2:shortest=1,drawtext=fontsize={fontsize}:fontfile={font}:fontcolor=white:text=\'{artist} - {title}\':x=W/2+W/10.5:y=H/11,drawtext=fontsize={fontsize}:fontfile={font}:fontcolor=white:text=\'{album}\':x=W/2+W/10.5:y=2*(H/10),drawtext=fontsize={fontsize}:fontfile={font}:fontcolor=white:text=\'{date}\':x=W/2+W/10.5:y=3*(H/10),format=yuv420p\" -c:v libx264 -r 30 -movflags +faststart \"{output}\"" 19 | os.system(command) 20 | 21 | def GetTags(sourcePath): 22 | global title 23 | global artist 24 | global date 25 | global album 26 | fileType = os.path.splitext(sourcePath)[1] 27 | metadata = None 28 | if(fileType == ".flac"): 29 | metadata = FLAC(sourcePath) 30 | elif (fileType == ".mp3"): 31 | metadata = EasyID3(sourcePath) 32 | title = metadata["title"][0] 33 | artist = metadata["artist"][0] 34 | date = metadata["date"][0] 35 | album = metadata["album"][0] 36 | print("Finished getting tags") 37 | 38 | 39 | if __name__ == '__main__': 40 | main() --------------------------------------------------------------------------------