Skip to content

Instantly share code, notes, and snippets.

@Laharah
Last active March 13, 2018 07:48
Show Gist options
  • Save Laharah/da1b4b51d68987fca8a1c70617b02344 to your computer and use it in GitHub Desktop.
Save Laharah/da1b4b51d68987fca8a1c70617b02344 to your computer and use it in GitHub Desktop.
Seperate active and non-active files from a directory according to uTorrent paths
#!/usr/bin/env python3
# The contents of this file are subject to the Python Software Foundation
# License Version 2.3 (the License). You may not copy or use this file, in
# either source code or executable form, except in compliance with the License.
# You may obtain a copy of the License at http://www.python.org/license.
#
# Software distributed under the License is distributed on an AS IS basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
from pathlib import Path
import os
import sys
import shutil
def get_default_resume_path():
"""
Checks the various common paths resume.dat may reside and returns a path to
it if it's found.
"""
print('Getting resume.dat path...')
app_datas = []
user_home = os.path.expanduser('~')
if os.getenv('APPDATA'):
app_datas.append(os.getenv('APPDATA'))
app_datas.append(os.path.join(user_home, 'Library', 'Application Support'))
app_datas.append('/opt')
app_datas.append(user_home)
for app_data in app_datas:
resume_path = os.path.join(app_data, 'uTorrent', 'resume.dat')
if not os.path.exists(resume_path) or not os.path.isfile(resume_path):
print('no resume.dat found at {0}...'.format(app_data))
else:
print('resume.dat found at {0}'.format(resume_path))
return resume_path
print('no resume.dat could be found')
return None
def get_active_paths_from_resume_dat(path):
"""given the path to resume.dat, decode and return 'path' items"""
if not os.path.exists(path):
er = ("{0} could not be found. Please check the file exists and "
"that you have permission to read it.".format(path))
print(er)
raise AssertionError(er)
if not os.path.isfile(path):
er = '{0} is a folder, "Path to resume.dat" must be a file.'.format(path)
print(er)
raise AssertionError(er)
try:
with open(path, 'rb') as f:
raw = f.read()
except (IOError, OSError) as e:
print('Could not open {0}. Reason{1}'.format(path, e))
raise
data = bdecode(raw)
paths = set()
for info in data.values():
try:
path = Path(info[b'path'].decode('utf8'))
except UnicodeDecodeError:
print("Could not decode file path: {}".format(info[b'path']))
continue
except TypeError:
continue
if not path.exists():
print("{} is listed as a uTorrent path, but it does not exist.".format(path))
continue
else:
paths.add(path.absolute())
return paths
def get_unref_items(target, ut_paths):
"return a set of paths from target that are not referenced by uTorrent"
target = Path(target)
if not target.exists():
raise AssertionError('{} does not exsist or cannot be opened'.format(target))
all_items = set(p.absolute() for p in target.iterdir())
unref = all_items - ut_paths
return unref
#######
# bencode
#######
def decode_int(x, f):
f += 1
newf = x.index(b'e', f)
n = int(x[f:newf])
if x[f] == '-':
if x[f + 1] == '0':
raise ValueError
elif x[f] == '0' and newf != f + 1:
raise ValueError
return (n, newf + 1)
def decode_string(x, f):
colon = x.index(b':', f)
n = int(x[f:colon])
if x[f] == b'0' and colon != f + 1:
raise ValueError
colon += 1
return (x[colon:colon + n], colon + n)
def decode_list(x, f):
r, f = [], f + 1
while x[f] != ord('e'):
v, f = decode_func[x[f]](x, f)
r.append(v)
return (r, f + 1)
def decode_dict(x, f):
r, f = {}, f + 1
while x[f] != ord('e'):
k, f = decode_string(x, f)
r[k], f = decode_func[x[f]](x, f)
return (r, f + 1)
decode_func = {}
decode_func[ord('l')] = decode_list
decode_func[ord('d')] = decode_dict
decode_func[ord('i')] = decode_int
decode_func[ord('0')] = decode_string
decode_func[ord('1')] = decode_string
decode_func[ord('2')] = decode_string
decode_func[ord('3')] = decode_string
decode_func[ord('4')] = decode_string
decode_func[ord('5')] = decode_string
decode_func[ord('6')] = decode_string
decode_func[ord('7')] = decode_string
decode_func[ord('8')] = decode_string
decode_func[ord('9')] = decode_string
def bdecode(x):
try:
r, l = decode_func[x[0]](x, 0)
except (IndexError, KeyError, ValueError):
raise Exception("not a valid bencoded string")
return r
if __name__ == '__main__':
args = sys.argv[1:]
if len(args) < 1:
target = os.getcwd()
else:
target = args[0]
if len(args) == 2:
resume = args[1]
else:
print("no resume.dat given, locating...")
resume = get_default_resume_path()
active = get_active_paths_from_resume_dat(resume)
unref = get_unref_items(target, active)
if not unref:
print("No unreferenced files found. EXITING")
sys.exit(0)
print('\nFound Unreferenced Files in {}'.format(target))
for f in sorted(unref):
print(str(f))
ans = None
while ans not in ('y n yes no'.split()):
ans = input("Move unreferenced files into an 'unreferenced' folder? (Y\\N):")
ans = ans.lower()
if ans.startswith('n'):
print("FINISHED, 0 items moved.")
sys.exit(0)
print("Moving Files...")
target = Path(target).absolute()
ur = target / 'unreferenced'
if not ur.exists():
ur.mkdir()
for f in unref:
try:
shutil.move(str(f), str(ur))
except Exception as e:
print("error relocating {}: {}".format(f, e))
print("FINISHED")
sys.exit(0)
@Laharah
Copy link
Author

Laharah commented Mar 13, 2018

Given a target directory, identify and move files/folders that aren't being seeded from uTorrent and move them to a folder called 'unreferenced'. Requires Python 3.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment