This commit is contained in:
Khoa Nguyen Anh 2018-03-02 22:52:48 +07:00
commit ccf26a8079
10 changed files with 370 additions and 0 deletions

117
.gitignore vendored Normal file
View File

@ -0,0 +1,117 @@
# Created by https://www.gitignore.io/api/vim,python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule.*
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
### Vim ###
# swap
.sw[a-p]
.*.sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
# End of https://www.gitignore.io/api/vim,python

14
Pipfile Normal file
View File

@ -0,0 +1,14 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
tinytag = "*"
[dev-packages]

38
Pipfile.lock generated Normal file
View File

@ -0,0 +1,38 @@
{
"_meta": {
"hash": {
"sha256": "ea2b7228db265fdaab23703135a88d5c005bf4225443197f890173608a692406"
},
"host-environment-markers": {
"implementation_name": "cpython",
"implementation_version": "3.6.4",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "4.14.0-parrot13-amd64",
"platform_system": "Linux",
"platform_version": "#1 SMP Parrot 4.14.13-1parrot13 (2018-01-21)",
"python_full_version": "3.6.4",
"python_version": "3.6",
"sys_platform": "linux"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"tinytag": {
"hashes": [
"sha256:8c86ea2cf812a9aff2d61b04fe954bd3feaeb4053f59809b7d1e09ea03be7cd3"
],
"version": "==0.18.0"
}
},
"develop": {}
}

1
__init__.py Normal file
View File

@ -0,0 +1 @@
from musipy import musipy

2
common/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from .get_content import get_content
from .same_name_alert import same_name_alert

13
common/get_content.py Normal file
View File

@ -0,0 +1,13 @@
import os
def get_content(source):
files = []
folders = []
for f in os.listdir(source):
if os.path.isfile(os.path.join(source, f)):
files.append(f)
else:
folders.append(f)
return files, folders

24
common/same_name_alert.py Normal file
View File

@ -0,0 +1,24 @@
import os
def same_name_alert(oldfile, newfile):
print("Old: {}".format(oldfile))
print("New: {}".format(newfile))
print("File existed, overwrite?")
print("'y' for yes")
print("'m' to view more data")
print("'l' to listen to two song")
while True:
ans = input("Answer: ")
if ans == 'y':
os.rename(oldfile, newfile)
break
elif ans == 'm':
print("Old file data:")
print("New file data:")
elif ans == 'l':
print("Listen to song 1")
print("Listen to song 2")
else:
break
return

107
musipy.py Normal file
View File

@ -0,0 +1,107 @@
import os
from parser import Parser
from common import same_name_alert, get_content
from tinytag import TinyTag
class musipy:
def __init__(self):
# prepare data
self.data = {}
self.parser = Parser()
# run
self.run()
def run(self):
if self.parser.mode == 'sort':
self.collect()
self.move_files()
else:
pass
return
# sort files bases on attribute
def sort(self, f, tag):
# get attribute from tag
# using self.attr
tag = getattr(tag, self.parser.attr)
if tag in self.data:
self.data[tag].append(f)
else:
self.data[tag] = []
self.data[tag].append(f)
return
# move files to new destination based on attribute
def move_files(self):
for folder, tracks in self.data.items():
if folder is None:
folder = "Undefined"
if not folder:
folder = "Undefined"
new_folder = self.parser.output + '/' + folder
if not os.path.exists(new_folder):
os.makedirs(new_folder)
print("Folder: {}".format(folder))
moved_files = 0
total_files = len(tracks)
for track in tracks:
moved_files += 1
percent = int(moved_files / total_files * 100)
print("Processing ... {:3d}%".format(percent), end='\r')
new_file = new_folder + '/' + os.path.basename(track)
if track == new_file:
# after sort, stay the same
continue
if os.path.exists(new_file):
same_name_alert(track, new_file)
else:
os.rename(track, new_file)
print("")
return
# collect all files and store in self.data
def collect(self):
folder_queue = [self.parser.source]
home_path_len = len(folder_queue[0])
while (len(folder_queue) > 0):
current_folder = folder_queue[0]
folder_queue = folder_queue[1:]
if len(current_folder[home_path_len:]) == 0:
print("[+] Scan /")
else:
print("[+] Scan {}".format(current_folder[home_path_len:]))
files, folders = get_content(current_folder)
# skip folder named '.folder'
# generate full path
for folder in folders:
if folder[0] != '.':
full_path = current_folder + '/' + folder
folder_queue.append(full_path)
# work with files
for f in files:
try:
fp = current_folder + '/' + f # full path to file
tag = TinyTag.get(fp)
except LookupError:
continue
except:
print("Cannot get tag from file --> Skip\n\t{}".format(fp))
continue
self.sort(fp, tag)
if __name__ == "__main__":
muse = musipy()

1
parser/__init__.py Normal file
View File

@ -0,0 +1 @@
from .parser import Parser

53
parser/parser.py Normal file
View File

@ -0,0 +1,53 @@
import os
import getopt
import sys
class Parser():
def __init__(self):
argv = sys.argv[1:]
self.source = None
self.output = None
self.attr = None
self.mode = None
try:
opts, args = getopt.getopt(
argv, 'hs:o:a:m:',
['source=', 'output=', 'attribute=', 'mode='])
except getopt.GetoptError:
print('')
exit(0)
for opt, arg in opts:
if opt == '-h':
print("Help")
exit(0)
elif opt in ('-s', '--source'):
self.source = arg
elif opt in ('-o', '--output'):
self.output = arg
elif opt in ('-a', '--attribute'):
self.attr = arg
elif opt in ('-m', '--mode'):
self.mode = arg
else:
print("Unknown flag {} {}".format(opt, arg))
if self.source is None:
self.source = os.getcwd()
if self.output is None:
self.output = self.source + '/output'
if self.attr is None:
self.attr = 'album'
if self.mode is None:
self.mode = 'sort'
if __name__ == '__main__':
p = Parser()
print(p.source)
print(p.output)
print(p.attr)
print(p.mode)