From 058e3fe7621cca646325d2dedc6af08c39292b12 Mon Sep 17 00:00:00 2001 From: Levent Duivel Date: Wed, 23 Jun 2021 13:35:35 +0500 Subject: [PATCH] Initial commit --- .gitignore | 2 + Pipfile | 16 ++++ Pipfile.lock | 193 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 23 ++++++ legacy.py | 94 ++++++++++++++++++++++ main.py | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.spec | 33 ++++++++ 7 files changed, 577 insertions(+) create mode 100644 .gitignore create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 README.md create mode 100644 legacy.py create mode 100644 main.py create mode 100644 main.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2912d62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/dist/ +/build/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..a042389 --- /dev/null +++ b/Pipfile @@ -0,0 +1,16 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +parse = "*" +eyed3 = "*" +ffmpeg-python = "*" +pyinstaller = "*" +mutagen = "*" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..64a0e51 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,193 @@ +{ + "_meta": { + "hash": { + "sha256": "298dd79c62dc5270388c13321b35a57cc741bb43662b04fce47c0641ffac0f32" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "altgraph": { + "hashes": [ + "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa", + "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe" + ], + "version": "==0.17" + }, + "coverage": { + "extras": [ + "toml" + ], + "hashes": [ + "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", + "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6", + "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45", + "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a", + "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03", + "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529", + "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a", + "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a", + "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2", + "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6", + "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759", + "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53", + "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a", + "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4", + "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff", + "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502", + "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793", + "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb", + "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905", + "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821", + "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b", + "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81", + "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0", + "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b", + "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3", + "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184", + "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701", + "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a", + "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82", + "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638", + "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5", + "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083", + "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6", + "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90", + "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465", + "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a", + "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3", + "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e", + "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066", + "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf", + "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b", + "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae", + "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669", + "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873", + "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b", + "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6", + "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb", + "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160", + "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c", + "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079", + "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d", + "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'", + "version": "==5.5" + }, + "deprecation": { + "hashes": [ + "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", + "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a" + ], + "version": "==2.1.0" + }, + "eyed3": { + "hashes": [ + "sha256:4b5064ec0fb3999294cca0020d4a27ffe4f29149e8292fdf7b2de9b9cabb7518", + "sha256:97bd529384df1c3dbdd143d86bf1705729d97d862969a214696f9e32c32b5767" + ], + "index": "pypi", + "version": "==0.9.6" + }, + "ffmpeg-python": { + "hashes": [ + "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127", + "sha256:ac441a0404e053f8b6a1113a77c0f452f1cfc62f6344a769475ffdc0f56c23c5" + ], + "index": "pypi", + "version": "==0.2.0" + }, + "filetype": { + "hashes": [ + "sha256:353369948bb1c09b8b3ea3d78390b5586e9399bff9aab894a1dff954e31a66f6", + "sha256:da393ece8d98b47edf2dd5a85a2c8733e44b769e32c71af4cd96ed8d38d96aa7" + ], + "version": "==1.0.7" + }, + "future": { + "hashes": [ + "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.18.2" + }, + "mutagen": { + "hashes": [ + "sha256:6397602efb3c2d7baebd2166ed85731ae1c1d475abca22090b7141ff5034b3e1", + "sha256:9c9f243fcec7f410f138cb12c21c84c64fde4195481a30c9bfb05b5f003adfed" + ], + "index": "pypi", + "version": "==1.45.1" + }, + "packaging": { + "hashes": [ + "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", + "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==20.9" + }, + "parse": { + "hashes": [ + "sha256:9ff82852bcb65d139813e2a5197627a94966245c897796760a3a2a8eb66f020b" + ], + "index": "pypi", + "version": "==1.19.0" + }, + "pefile": { + "hashes": [ + "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645" + ], + "markers": "sys_platform == 'win32'", + "version": "==2019.4.18" + }, + "pyinstaller": { + "hashes": [ + "sha256:f5c0eeb2aa663cce9a5404292c0195011fa500a6501c873a466b2e8cad3c950c" + ], + "index": "pypi", + "version": "==4.2" + }, + "pyinstaller-hooks-contrib": { + "hashes": [ + "sha256:27558072021857d89524c42136feaa2ffe4f003f1bdf0278f9b24f6902c1759c", + "sha256:892310e6363655838485ee748bf1c5e5cade7963686d9af8650ee218a3e0b031" + ], + "version": "==2021.1" + }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.4.7" + }, + "pywin32-ctypes": { + "hashes": [ + "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", + "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98" + ], + "markers": "sys_platform == 'win32'", + "version": "==0.2.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "version": "==0.10.2" + } + }, + "develop": {} +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ec50ad --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +osu!pl +====== +Export your osu! beatmaps as Music & Video Libraries and playlists + +Requirements +------------ +1. Install [ffmpeg](https://www.ffmpeg.org/) to PATH +2. For building, you gonna need pipenv: `pip install pipenv` + +Install (Windows) +----------------- +Visit [releases](https://github.com/mostm/osu-pl/releases/latest), and grab latest one. + +Build +----- +1. `git clone https://github.com/mostm/osu-pl.git` +2. `pipenv sync` +3. `pyinstaller main.spec` + +Note: While project has been written to support multiplatform, osu!stable does not officially support multiplatform. + +So there is no reason for me to test this under other platforms. +Submit your pull requests, if this becomes an issue for you. diff --git a/legacy.py b/legacy.py new file mode 100644 index 0000000..161e123 --- /dev/null +++ b/legacy.py @@ -0,0 +1,94 @@ +import os +import shutil +import configparser + +beatmap_dir = os.path.abspath(os.environ['LOCALAPPDATA']+'\\osu!\\Songs\\') +beatmaps = [] +bm_osu = [] + +with os.scandir(os.path.abspath(beatmap_dir)) as it: + for entry in it: + if entry.is_dir(): + try: + beatmap_id = int(str(entry.name).split(' ')[0]) + except ValueError: + # I'm not sure what to do about unranked maps right now, we will exclude them + continue + beatmaps.append(entry.path) + +beatmap_type = { + "id": 0, # You may parse for "[Metadata]\n\nBeatmapSetID:{sid}" (WARN: Earlier maps will lack this parameter (osu file format v3 < osu file format v14)) or use the one provided with path + "name": 'Author - Title', # I should get it from osu files rather than directory, but that's how it happens + "audio": ".\\somefile.mp3", # Parse for "[General]\n\nAudioFilename: {filename}" | DONE + "video": ".\\something.mp4" # Parse for "[Events]\n\nVideo,{timestamp},{filename}" (found mp4,avi,mpg) | plz check, TODO +} + +for beatmap in beatmaps: + with os.scandir(os.path.abspath(beatmap)) as it: + bm = { + 'id': int(str(os.path.split(beatmap)[1]).split(' ')[0]), + 'name': str(os.path.split(beatmap)[1])[len(str(os.path.split(beatmap)[1]).split(' ')[0])+1:], + 'audio': None, + 'audio_length': None, + 'video': None + } + print('{} {}'.format(bm['id'], bm['name'])) + for entry in it: + if entry.is_file(): + if entry.path.endswith('osu'): + # ConfigParser is actually overkill solution, although I set it up to work + # FixMe: This solution does not account for multiple (via diff) maps in one + # Although, ranked maps should never have this. + with open(entry.path, 'r', encoding="utf-8") as f: + config_string = '[global]\n' + f.read() + a = '' + for x in config_string.split('\n')[:config_string.split('\n').index('[Events]')-1]: + a += x+'\n' + config = configparser.ConfigParser(allow_no_value=True) + config.read_string(a) + # TODO: Rewrite to simple checks and add video checking. + bm['audio'] = os.path.abspath(os.path.dirname(entry.path)+'\\'+config.get('General', 'AudioFilename')) + elif entry.path.endswith('mp4') or entry.path.endswith('avi') or entry.path.endswith('mpg'): + bm['video'] = entry.path + bm_osu.append(bm) + + +text_playlist = "" +for bm in bm_osu: + if bm['audio']: + text_playlist += "#EXTINF:0,{0}\n{1}\n".format(bm['name'], bm['audio']) + +text_playlist = text_playlist[:-1] + +try: + with open('osu.m3u', 'w', encoding='utf-8') as file: + file.write(text_playlist) +except: + open('osu.m3u', 'x') + with open('osu.m3u', 'w', encoding='utf-8') as file: + file.write(text_playlist) + +text_type = "" +for bm in bm_osu: + if bm['name']: + text_type += "{0}\n".format(bm['name']) +text_type = text_type[:-1] +try: + with open('osu.txt', 'w', encoding='utf-8') as file: + file.write(text_type) +except: + open('osu.txt', 'x') + with open('osu.txt', 'w', encoding='utf-8') as file: + file.write(text_type) + +for bm in bm_osu: + if bm['audio']: + print('{} {}'.format(bm['id'], bm['name'])) + if os.path.basename(bm['audio']).split('.')[-1] != '': + shutil.copy2(bm['audio'], "{}\\osu music\\{}.{}".format(os.getcwd(), bm['name'], os.path.basename(bm['audio']).split('.')[-1])) + if bm['video']: + shutil.copy2(bm['video'], "{}\\osu music\\{}.{}".format(os.getcwd(), bm['name'], os.path.basename(bm['video']).split('.')[-1])) + + + +print('done, ty for use') \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..6748952 --- /dev/null +++ b/main.py @@ -0,0 +1,216 @@ +import os +import shutil +from pathlib import Path +import mutagen +from mutagen.easyid3 import EasyID3 +from mutagen.id3 import ID3, APIC +import ffmpeg +import mimetypes + +EasyID3.RegisterTextKey('comment', 'COMM') + + +def parse_beatmap(content): + section = None + section_content = None + pure_sections = ['events', 'timingpoints'] + beatmap = {} + for line in content.split('\n'): + if line.startswith('//'): + continue + if line.startswith('['): + if section and section_content: + beatmap[section] = section_content + section = line[1:line.index(']')].lower() + section_content = dict() + if section in pure_sections: + section_content = list() + continue + if section in pure_sections: + section_content.append(line) + elif section and ':' in line: + key = line[:line.index(':')].lower() + value = line[line.index(':') + 1:].strip() + section_content[key] = value + return beatmap + + +def scan_beatmaps(root): + beatmap_sets = {} + for beatmap_path in Path(root).glob('**/*.osu'): + file = open(str(beatmap_path), 'r', encoding='utf-8').read() + beatmap = parse_beatmap(file) + beatmap_set = beatmap['metadata'].get('beatmapsetid') + if not beatmap['metadata'].get('beatmapsetid'): + p = beatmap_path.parent + beatmap_set = p.name.split(' ')[0] + if not beatmap_set.isdigit(): + beatmap_set = 'Unranked' + bg = None + video = None + for event in beatmap['events']: + if event.startswith('0,0,'): + bg = event.split(',')[2].strip('"').strip() + if event.startswith('Video'): + video = [x.strip('"') for x in event.split(',')[1:]] + video = {'timing': video[0], 'filename': video[1]} + beatmap['background'] = bg + beatmap['video'] = video + beatmap['path'] = str(beatmap_path) + if not beatmap_sets.get(beatmap_set): + beatmap_sets[beatmap_set] = list() + beatmap_sets[beatmap_set].append(beatmap) + return beatmap_sets + + +def generalize_beatmap_sets(beatmap_sets): + # Generalize to only relevant data + generalized = {} + unique_warn = [] + if beatmap_sets.get('Unranked'): + beatmap_sets.pop('Unranked') + for setid in beatmap_sets.keys(): + set_data = {} + for map in beatmap_sets[setid]: + data = map['metadata'] + data.pop('version') + if data.get('beatmapid'): + data.pop('beatmapid') + data.pop('beatmapsetid') + beatmap_dir = Path(map['path']).parent + data['audio'] = None + if map['general']['audiofilename']: + data['audio'] = str(beatmap_dir.joinpath(map['general']['audiofilename']).absolute()) + data['video'] = map['video'] + if data['video']: + data['video']['filename'] = str(beatmap_dir.joinpath(data['video']['filename']).absolute()) + if not os.path.exists(data['video']['filename']): + if setid not in unique_warn: + print(f'Video for {setid} is mentioned, but doesn\'t exist!') + unique_warn.append(setid) + data['video'] = None + data['thumbnail'] = None + if map['background']: + data['thumbnail'] = str(beatmap_dir.joinpath(map['background']).absolute()) + for k, v in data.items(): + if not v: + continue + if not set_data.get(k) or all([k == 'thumbnail', v != '', v]): + set_data[k] = v + if set_data.get(k) != v: + if k == 'tags' and len(v) > len(set_data.get(k)): + set_data[k] = v + # print(f"{map['path']}: Conflict of data with set ({k}) | {set_data.get(k)} != {v}") + generalized[setid] = set_data + return generalized + + +def clean_and_allow_filename(dirty_filename, invalid='<>:"/\|?*'): + fn = str(dirty_filename) + for char in invalid: + fn = fn.replace(char, '') + return fn + + +def generate_library(beatmap_sets, music=True, video=False, music_target=None, video_target=None): + if music: + if not music_target: + music_target = f'{os.getcwd()}{os.path.sep}osu!MusicLibrary' + try: + os.mkdir(f"{music_target}") + except: + pass + for setid, beatmap in beatmap_sets.items(): + try: + map = dict(beatmap) + dir_name = f"{map['title']} by {map['artist']} ({map['creator']})" + dir_name = clean_and_allow_filename(dir_name) + try: + os.mkdir(f"{music_target}{os.path.sep}{dir_name}") + except: + pass + if not map['audio'] or map['audio'] == 'virtual': + continue + ext = os.path.splitext(map['audio'])[1] + fn = f"{map['artist']} - {map['title']}{ext}" + fn = clean_and_allow_filename(fn) + file_target = f"{music_target}{os.path.sep}{dir_name}{os.path.sep}{fn}" + shutil.copy2(map['audio'], file_target) + map['audio'] = file_target + if map.get('thumbnail') and os.path.exists(map['thumbnail']): + ext = os.path.splitext(map['thumbnail'])[1] + file_target = f"{music_target}{os.path.sep}{dir_name}{os.path.sep}cover{ext}" + shutil.copy2(map['thumbnail'], file_target) + map['thumbnail'] = file_target + audiofile = mutagen.File(map['audio'], easy=True) + if audiofile: + audiofile['artist'] = map['artist'] + audiofile['album'] = map['creator'] + audiofile['albumartist'] = map.get('artistunicode') if map.get('artistunicode') else map['artist'] + audiofile['title'] = map['title'] + if map.get('tags'): + audiofile['comment'] = map['tags'] + audiofile['tracknumber'] = ['1', '1'] + audiofile.save() + if map.get('thumbnail') and not map['audio'].endswith('.ogg'): + audio = mutagen.File(map['audio'], easy=False) + if 'audio/vorbis' in audio.mime: + continue + with open(map.get('thumbnail'), 'rb') as albumart: + audio['APIC'] = APIC( + encoding=3, + mime=mimetypes.guess_type(map.get('thumbnail')), + type=3, desc='osu! Beatmap Thumbnail', + data=albumart.read() + ) + audio.save() + except Exception as error: + print(f'[Music] Failure while processing {setid} | {type(error)} | {str(error)}') + if video: + if not video_target: + video_target = f'{os.getcwd()}{os.path.sep}osu!VideoLibrary' + try: + os.mkdir(f"{video_target}") + except: + pass + for setid, beatmap in beatmap_sets.items(): + try: + if not beatmap.get('video') or beatmap.get('audio').endswith('virtual'): + continue + map = dict(beatmap) + fn = f"{map['artist']} - {map['title']} ({map['creator']}).mp4" + output_fp = f"{video_target}{os.path.sep}{clean_and_allow_filename(fn)}" + audio_in = ffmpeg.input(map['audio'])['a'] + kw = {} + if map['video']['timing'] != '0': + kw['itsoffset'] = float(map['video']['timing']) / 1000 + video_in = ffmpeg.input(map['video']['filename'], **kw) + streams = ffmpeg.probe(map['video']['filename'])['streams'] + ow = False + for stream in streams: + if stream.get('codec_name') in ['h264', 'avc1', 'mpeg4']: + video_in = video_in[str(stream['index'])] + ow = True + if not ow: + for stream in streams: + if stream.get('codec_name') not in ['h264', 'avc1', 'mpeg4']: + output_fp = output_fp[:-len('mp4')] + 'mkv' + out = ffmpeg.output(audio_in, video_in, output_fp, vcodec='copy', acodec='copy', fflags='+genpts') + print(' '.join(out.compile())) + out.run(overwrite_output=True) + except Exception as error: + print(f'[Video] Failure while processing {setid} | {type(error)} | {str(error)}') + return + + +if __name__ == '__main__': + root = os.path.abspath(os.environ['LOCALAPPDATA'] + '\\osu!\\Songs\\') + print('Scanning Beatmaps') + beatmap_sets = scan_beatmaps(root) + print('Generalizing beatmap data') + beatmaps = generalize_beatmap_sets(beatmap_sets) + print('Generating Music & Video Libraries') + generate_library(beatmaps, + music=input('Music Library? (y/n) ').lower().startswith('y'), + video=input('Video Library? (y/n) ').lower().startswith('y')) + print('Done, thanks for usage!') diff --git a/main.spec b/main.spec new file mode 100644 index 0000000..b2cd926 --- /dev/null +++ b/main.spec @@ -0,0 +1,33 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + + +a = Analysis(['main.py'], + pathex=['C:\\Users\\Levent\\Stuff\\Projects\\osu!pl'], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='main', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True )