From 9eee57dd198820d6a8f26a7a51dc69875e2cdde7 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Sat, 27 Apr 2024 15:15:27 +0200 Subject: [PATCH 01/12] chore: add interactive argument --- mkpl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkpl.py b/mkpl.py index 32a8ada..ad2da46 100644 --- a/mkpl.py +++ b/mkpl.py @@ -186,6 +186,9 @@ def get_args(): parser.add_argument( "-S", "--split", help="Split playlist by directories", action="store_true" ) + parser.add_argument( + "-R", "--interactive", help="Asks each file for confirmation", action="store_true" + ) orderby_group.add_argument( "-s", "--shuffle", help="Casual order", action="store_true" ) From 44e85097e673af46c1a22873ca94668673151b0a Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Sun, 28 Apr 2024 15:20:11 +0200 Subject: [PATCH 02/12] chore: add interactive argument into make_playlist function --- mkpl.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mkpl.py b/mkpl.py index ad2da46..08dd3d0 100644 --- a/mkpl.py +++ b/mkpl.py @@ -430,6 +430,7 @@ def make_playlist( absolute=False, min_size=1, windows=False, + interactive=False, verbose=False, ): """Make playlist list""" @@ -474,6 +475,8 @@ def make_playlist( # Check file size if size <= min_size: continue + if interactive: + pass vprint(verbose, f"add multimedia file {file}") filelist.append(unix_to_dos(file) if windows else file) # Check sort @@ -594,6 +597,7 @@ def main(): absolute=args.absolute, min_size=args.size, windows=args.windows, + interactive=args.interactive, verbose=args.verbose, ) From 75cff3bd283c3b210c44f7263a08f5ce3f049494 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Mon, 29 Apr 2024 15:39:11 +0200 Subject: [PATCH 03/12] chore: add confirm function for interactive mode --- mkpl.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/mkpl.py b/mkpl.py index 08dd3d0..e14a2a6 100644 --- a/mkpl.py +++ b/mkpl.py @@ -268,6 +268,28 @@ def get_args(): return args +def confirm(file, default="y"): + """Ask user to enter Y or N (case-insensitive) + + :file: file to add into playlist + :default: default answer + :return: True if the answer is Y. + :rtype: bool + """ + while ( + answer := input( + "Add file {0} to playlist? {1}:".format( + file, "[Y/n]" if default == "y" else "[y/N]" + ) + ).lower() + ) not in ("y", "n"): + # Check if default + if not answer: + answer = default + break + return answer == "y" + + def file_in_playlist(playlist, file, root=None): """Check if file is in the playlist""" for f in playlist: @@ -476,7 +498,8 @@ def make_playlist( if size <= min_size: continue if interactive: - pass + if not confirm(file): + continue vprint(verbose, f"add multimedia file {file}") filelist.append(unix_to_dos(file) if windows else file) # Check sort From 5a8fa886eb8a67e9e80e79dd91e349d3f4451d05 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Tue, 30 Apr 2024 10:44:54 +0200 Subject: [PATCH 04/12] chore: add count argument --- mkpl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkpl.py b/mkpl.py index e14a2a6..5c5e4f6 100644 --- a/mkpl.py +++ b/mkpl.py @@ -189,6 +189,9 @@ def get_args(): parser.add_argument( "-R", "--interactive", help="Asks each file for confirmation", action="store_true" ) + parser.add_argument( + "-C", "--count", help="Count elements into playlist", action="store_true" + ) orderby_group.add_argument( "-s", "--shuffle", help="Casual order", action="store_true" ) From 6cf9b585267f9d71060493365d8cff4ea32f97dc Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Wed, 1 May 2024 10:01:25 +0200 Subject: [PATCH 05/12] chore: add count multimedia files --- mkpl.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mkpl.py b/mkpl.py index 5c5e4f6..ed0a11d 100644 --- a/mkpl.py +++ b/mkpl.py @@ -641,6 +641,10 @@ def main(): _process_playlist(multimedia_files, args) + # Count files into playlist + if args.count: + print(len([file for file in multimedia_files if not file.startswith("#")])) + except Exception as err: report_issue(err) From eac615e4e31a57c9cd4c57ce385dd7cc28a921a9 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Thu, 2 May 2024 10:03:38 +0200 Subject: [PATCH 06/12] chore: add order by size argument --- mkpl.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mkpl.py b/mkpl.py index ed0a11d..691b9c9 100644 --- a/mkpl.py +++ b/mkpl.py @@ -213,6 +213,12 @@ def get_args(): help="Order playlist files by year", action="store_true", ) + orderby_group.add_argument( + "-Z", + "--orderby-size", + help="Order playlist files by size", + action="store_true", + ) args = parser.parse_args() From 68a15d45b5409915f37336f14953c57251bd7187 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Fri, 3 May 2024 10:10:03 +0200 Subject: [PATCH 07/12] chore: add order by size into make_playlist function --- mkpl.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mkpl.py b/mkpl.py index 691b9c9..b06a74e 100644 --- a/mkpl.py +++ b/mkpl.py @@ -286,11 +286,11 @@ def confirm(file, default="y"): :rtype: bool """ while ( - answer := input( - "Add file {0} to playlist? {1}:".format( - file, "[Y/n]" if default == "y" else "[y/N]" - ) - ).lower() + answer := input( + "Add file {0} to playlist? {1}:".format( + file, "[Y/n]" if default == "y" else "[y/N]" + ) + ).lower() ) not in ("y", "n"): # Check if default if not answer: @@ -455,6 +455,7 @@ def make_playlist( sortby_date=False, sortby_track=False, sortby_year=False, + sortby_size=False, recursive=False, exclude_dirs=None, unique=False, @@ -520,6 +521,8 @@ def make_playlist( filelist = sorted(filelist, key=get_track) elif sortby_year: filelist = sorted(filelist, key=get_year) + elif sortby_size: + filelist = sorted(filelist, key=os.path.getsize) return filelist @@ -623,6 +626,7 @@ def main(): sortby_date=args.orderby_date, sortby_track=args.orderby_track, sortby_year=args.orderby_year, + sortby_size=args.orderby_size, recursive=args.recursive, exclude_dirs=args.exclude_dirs, unique=args.unique, From a261b8cd7eece198da9299fec3bda449e5890ef0 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Sat, 4 May 2024 11:44:27 +0200 Subject: [PATCH 08/12] chore: add orderby length argument --- mkpl.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mkpl.py b/mkpl.py index b06a74e..d87075a 100644 --- a/mkpl.py +++ b/mkpl.py @@ -219,6 +219,12 @@ def get_args(): help="Order playlist files by size", action="store_true", ) + orderby_group.add_argument( + "-L", + "--orderby-length", + help="Order playlist files by length", + action="store_true", + ) args = parser.parse_args() From 87d64589bf8b1a5e2574bbc859957b573e655675 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Sun, 5 May 2024 12:04:57 +0200 Subject: [PATCH 09/12] chore: add get_length function --- mkpl.py | 92 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/mkpl.py b/mkpl.py index d87075a..f945c41 100644 --- a/mkpl.py +++ b/mkpl.py @@ -151,7 +151,7 @@ def get_args(): "--link", help="Add local or remote file links", nargs=argparse.ONE_OR_MORE, - metavar='FILES', + metavar="FILES", default=[], ) parser.add_argument( @@ -159,7 +159,7 @@ def get_args(): "--join", help="Join one or more other playlist files", nargs=argparse.ONE_OR_MORE, - metavar='PLAYLISTS', + metavar="PLAYLISTS", default=[], ) parser.add_argument( @@ -187,7 +187,10 @@ def get_args(): "-S", "--split", help="Split playlist by directories", action="store_true" ) parser.add_argument( - "-R", "--interactive", help="Asks each file for confirmation", action="store_true" + "-R", + "--interactive", + help="Asks each file for confirmation", + action="store_true", ) parser.add_argument( "-C", "--count", help="Count elements into playlist", action="store_true" @@ -292,11 +295,11 @@ def confirm(file, default="y"): :rtype: bool """ while ( - answer := input( - "Add file {0} to playlist? {1}:".format( - file, "[Y/n]" if default == "y" else "[y/N]" - ) - ).lower() + answer := input( + "Add file {0} to playlist? {1}:".format( + file, "[Y/n]" if default == "y" else "[y/N]" + ) + ).lower() ) not in ("y", "n"): # Check if default if not answer: @@ -327,7 +330,9 @@ def join_playlist(playlist, *others): try: # open playlist, remove extensions and extend current playlist file lines = open(file).readlines() - playlist.extend([line.rstrip() for line in lines if not line.startswith('#')]) + playlist.extend( + [line.rstrip() for line in lines if not line.startswith("#")] + ) except FileNotFoundError: print(f"warning: {file} file not found") except OSError as err: @@ -373,6 +378,13 @@ def get_year(file): return file.tags.get("TDOR", default)[0] +def get_length(file): + """Get file by length for sort""" + file = open_multimedia_file(file) + if file and hasattr(file, "info"): + return file.info.length if hasattr(file.info, "length") else 0.0 + + def find_pattern(pattern, path): """Find patter in a file and tags""" global AUDIO_FORMAT @@ -384,7 +396,7 @@ def find_pattern(pattern, path): if pattern.findall(path): return True # Check type of file - ext = os.path.splitext(path)[1].replace('.', '').lower() + ext = os.path.splitext(path)[1].replace(".", "").lower() if ext in AUDIO_FORMAT: file = open_multimedia_file(path) # Check supports of ID3 tagsadd compiled pattern @@ -421,23 +433,23 @@ def unix_to_dos(path, viceversa=False): def write_playlist( - playlist, - open_mode, - files, - encoding, - enabled_extensions=False, - image=None, - ext_part=None, - max_tracks=None, - verbose=False, + playlist, + open_mode, + files, + encoding, + enabled_extensions=False, + image=None, + ext_part=None, + max_tracks=None, + verbose=False, ): """Write playlist into file""" if playlist: with open( - playlist, - mode=open_mode, - encoding="UTF-8" if encoding == "UNICODE" else encoding, - errors="ignore", + playlist, + mode=open_mode, + encoding="UTF-8" if encoding == "UNICODE" else encoding, + errors="ignore", ) as pl: if image and enabled_extensions: vprint(verbose, f"set image {image}") @@ -454,22 +466,22 @@ def write_playlist( def make_playlist( - directory, - file_formats, - pattern=None, - sortby_name=False, - sortby_date=False, - sortby_track=False, - sortby_year=False, - sortby_size=False, - recursive=False, - exclude_dirs=None, - unique=False, - absolute=False, - min_size=1, - windows=False, - interactive=False, - verbose=False, + directory, + file_formats, + pattern=None, + sortby_name=False, + sortby_date=False, + sortby_track=False, + sortby_year=False, + sortby_size=False, + recursive=False, + exclude_dirs=None, + unique=False, + absolute=False, + min_size=1, + windows=False, + interactive=False, + verbose=False, ): """Make playlist list""" filelist = list() @@ -507,7 +519,7 @@ def make_playlist( # Check if file is in playlist if unique: if file_in_playlist( - filelist, file, root=root if not absolute else None + filelist, file, root=root if not absolute else None ): continue # Check file size From 9c7da150c1373103e4c938accda6cb79ec39f702 Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Mon, 6 May 2024 12:13:51 +0200 Subject: [PATCH 10/12] chore: add order by length into make_playlist function --- mkpl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mkpl.py b/mkpl.py index f945c41..b8ba01d 100644 --- a/mkpl.py +++ b/mkpl.py @@ -399,7 +399,7 @@ def find_pattern(pattern, path): ext = os.path.splitext(path)[1].replace(".", "").lower() if ext in AUDIO_FORMAT: file = open_multimedia_file(path) - # Check supports of ID3 tagsadd compiled pattern + # Check supports of ID3 tags add compiled pattern if file and hasattr(file, "ID3"): # Check pattern into title if file.tags.get("TIT2"): @@ -474,6 +474,7 @@ def make_playlist( sortby_track=False, sortby_year=False, sortby_size=False, + sortby_length=False, recursive=False, exclude_dirs=None, unique=False, @@ -541,6 +542,8 @@ def make_playlist( filelist = sorted(filelist, key=get_year) elif sortby_size: filelist = sorted(filelist, key=os.path.getsize) + elif sortby_length: + filelist = sorted(filelist, key=get_length) return filelist @@ -645,6 +648,7 @@ def main(): sortby_track=args.orderby_track, sortby_year=args.orderby_year, sortby_size=args.orderby_size, + sortby_length=args.orderby_length, recursive=args.recursive, exclude_dirs=args.exclude_dirs, unique=args.unique, From 8f0e437b140cab79b3ccfa28e53004a49416f48b Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Tue, 7 May 2024 11:53:48 +0200 Subject: [PATCH 11/12] chore: add new arguments --- README.md | 78 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index dceaad3..941bfb8 100644 --- a/README.md +++ b/README.md @@ -22,32 +22,36 @@ $ pip install . # for others ``mkpl`` have many command line arguments. They are explained in this table: -| short | long | description | args | -|-------|-----------------|-----------------------------------------------|---------------------------| -| -d | --directories | Directories that contains multimedia file | Path of directories | -| -e | --exclude-dirs | Exclude directory paths | Path of directories | -| -i | --include | Include other file format | Format of file. ex. mp3 | -| -p | --pattern | Regular expression inclusion pattern | Regular expression string | -| -f | --format | Select only a file format | Format of file. ex. mp3 | -| -s | --size | Start size in bytes | Bytes number | -| -m | --max-tracks | Maximum number of tracks | Number | -| -t | --title | Playlist title | Title string | -| -g | --encoding | Text encoding | UTF-8,ASCII,UNICODE | -| -I | --image | Playlist image | Image path | -| -l | --link | Add local or remote files | Files | -| -j | --join | Join one or more other playlist files | Playlist files | -| -r | --recursive | Recursive search | | -| -a | --absolute | Absolute file name | | -| -s | --shuffle | Casual order | | -| -u | --unique | The same files are not placed in the playlist | | -| -c | --append | Continue playlist instead of override it | | -| -w | --windows | Windows style folder separator | | -| -v | --verbose | Enable verbosity (debug mode) | | -| -S | --split | Split playlist by directories | | -| -o | --orderby-name | Order playlist files by name | | -| -O | --orderby-date | Order playlist files by creation date | | -| -T | --orderby-track | Order playlist files by track | | -| -y | --orderby-year | Order playlist files by year | | +| short | long | description | args | +|-------|------------------|-----------------------------------------------|---------------------------| +| -d | --directories | Directories that contains multimedia file | Path of directories | +| -e | --exclude-dirs | Exclude directory paths | Path of directories | +| -i | --include | Include other file format | Format of file. ex. mp3 | +| -p | --pattern | Regular expression inclusion pattern | Regular expression string | +| -f | --format | Select only a file format | Format of file. ex. mp3 | +| -s | --size | Start size in bytes | Bytes number | +| -m | --max-tracks | Maximum number of tracks | Number | +| -t | --title | Playlist title | Title string | +| -g | --encoding | Text encoding | UTF-8,ASCII,UNICODE | +| -I | --image | Playlist image | Image path | +| -l | --link | Add local or remote files | Files | +| -j | --join | Join one or more other playlist files | Playlist files | +| -r | --recursive | Recursive search | | +| -a | --absolute | Absolute file name | | +| -s | --shuffle | Casual order | | +| -u | --unique | The same files are not placed in the playlist | | +| -c | --append | Continue playlist instead of override it | | +| -w | --windows | Windows style folder separator | | +| -v | --verbose | Enable verbosity (debug mode) | | +| -S | --split | Split playlist by directories | | +| -R | --interactive | Asks each file for confirmation | | +| -C | --count | Count elements into playlist | | +| -o | --orderby-name | Order playlist files by name | | +| -O | --orderby-date | Order playlist files by creation date | | +| -T | --orderby-track | Order playlist files by track | | +| -y | --orderby-year | Order playlist files by year | | +| -Z | --orderby-size | Order playlist files by size | | +| -L | --orderby-length | Order playlist files by length | | ## Examples @@ -146,19 +150,39 @@ $ pip install . # for others ... ``` -15. Sort playlist files by name (`-o`), by creation date (`-O`), by track number (`-T`) or by year (`-y`): +15. Sort playlist files by name (`-o`), by creation date (`-O`), by track number (`-T`), by year (`-y`), by size (`-Z`) or by length (`-L`): ```bash mkpl -d "new_collection" -r "my music.m3u" -o mkpl -d "new_collection" -r "my music.m3u" -O mkpl -d "new_collection" -r "my music.m3u" -T mkpl -d "new_collection" -r "my music.m3u" -y + mkpl -d "new_collection" -r "my music.m3u" -Z + mkpl -d "new_collection" -r "my music.m3u" -L ``` 16. Join the _"First playlist.m3u"_ and _"Second playlist.m3u8"_ with new **"Third playlist.m3u"**: ```bash mkpl -d "new_collection" -r "Third playlist" -j "First playlist.m3u" "Second playlist.m3u8" + ``` + +17. Counts the multimedia files: + + ```console + mkpl -d "new_collection" -r "My new collection" -C + 4023 + ``` + +18. Asks confirmation for every file into folders: + + ```console + mkpl -d "new_collection" -r "My new collection" -R + Add file new_collection/sample1.mp3 to playlist? [Y/n]:y + Add file new_collection/sample2.mp3 to playlist? [Y/n]:Y + Add file new_collection/sample3.mp3 to playlist? [Y/n]:n + Add file new_collection/sample4.mp3 to playlist? [Y/n]:N + ``` ## Use it like Python module From ccca9860b22364859d5649f82bfa06297d8103cb Mon Sep 17 00:00:00 2001 From: Matteo Guadrini Date: Wed, 8 May 2024 09:59:12 +0200 Subject: [PATCH 12/12] chore: new minor version --- CHANGES.md | 9 +++++++++ mkpl.py | 4 ++-- pyproject.toml | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cb42f46..eae0731 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,14 @@ # Release notes +## 1.9.0 +May 09, 2024 + +- Add `orderby-size` cli argument +- Add `orderby-length` cli argument +- Add `interactive` cli argument +- Add `count` cli argument +- Add **confirm** function for _interactive_ mode + ## 1.8.0 Aug 03, 2023 diff --git a/mkpl.py b/mkpl.py index b8ba01d..6b39c0b 100644 --- a/mkpl.py +++ b/mkpl.py @@ -5,7 +5,7 @@ # created by: matteo.guadrini # mkpl -- mkpl # -# Copyright (C) 2023 Matteo Guadrini +# Copyright (C) 2024 Matteo Guadrini # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -71,7 +71,7 @@ "f4a", } FILE_FORMAT = AUDIO_FORMAT.union(VIDEO_FORMAT) -__version__ = "1.8.0" +__version__ = "1.9.0" # endregion diff --git a/pyproject.toml b/pyproject.toml index 13409d7..d9b0317 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ py-modules = ["mkpl"] [project] name = "make_playlist" -version = "1.8.0" +version = "1.9.0" readme = "README.md" authors = [{ name = "Matteo Guadrini", email = "matteo.guadrini@hotmail.it" }] @@ -16,7 +16,7 @@ maintainers = [ ] description = "Make M3U format playlist from command line." -requires-python = ">=3.6" +requires-python = ">=3.7" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",