1
|
|
|
#!/usr/bin/python |
2
|
|
|
# -*- coding: utf-8 -*- |
3
|
1 |
|
import datetime |
4
|
1 |
|
import os |
5
|
1 |
|
import re |
6
|
1 |
|
from pathlib import Path |
7
|
1 |
|
from stat import ST_CTIME, ST_MTIME, ST_SIZE |
8
|
1 |
|
from typing import Generator |
9
|
|
|
|
10
|
1 |
|
from saucenao.files.constraint import Constraint |
11
|
|
|
|
12
|
|
|
|
13
|
1 |
|
class Filter: |
14
|
|
|
""" |
15
|
|
|
Filter to apply constraints for multiple metadata entries of the files. |
16
|
|
|
Obviously the files have to exist or they'll automatically get removed. |
17
|
|
|
Returns a generator object |
18
|
|
|
""" |
19
|
|
|
|
20
|
1 |
|
_directory = None |
21
|
1 |
|
_file_system_objects = None |
22
|
|
|
|
23
|
1 |
|
def __init__(self, assert_is_folder=False, assert_is_file=False, creation_date=None, modified_date=None, name=None, |
24
|
|
|
file_type=None, size=None): |
25
|
|
|
"""Initializing function |
26
|
|
|
|
27
|
|
|
:type assert_is_folder: bool |
28
|
|
|
:type assert_is_file: bool |
29
|
|
|
:type creation_date: Constraint |
30
|
|
|
:type modified_date: Constraint |
31
|
|
|
:type name: Constraint |
32
|
|
|
:type file_type: Constraint |
33
|
|
|
:type size: Constraint |
34
|
|
|
""" |
35
|
1 |
|
self._filter_assert_is_folder = assert_is_folder |
36
|
1 |
|
self._filter_assert_is_file = assert_is_file |
37
|
1 |
|
self._filter_creation_date = creation_date |
38
|
1 |
|
self._filter_modified_date = modified_date |
39
|
1 |
|
self._filter_name = name |
40
|
1 |
|
self._filter_file_type = file_type |
41
|
1 |
|
self._filter_file_size = size |
42
|
|
|
|
43
|
1 |
|
@property |
44
|
|
|
def file_system_objects(self): |
45
|
|
|
"""Property for file system objects |
46
|
|
|
|
47
|
|
|
:return: |
48
|
|
|
""" |
49
|
1 |
|
if self._file_system_objects is None: |
50
|
1 |
|
if self._directory: |
51
|
1 |
|
return os.listdir(self._directory) |
52
|
|
|
else: |
53
|
1 |
|
return [] |
54
|
1 |
|
return self._file_system_objects |
55
|
|
|
|
56
|
1 |
|
@staticmethod |
57
|
1 |
|
def _get_timestamp_from_datestring(date_string) -> float: |
58
|
|
|
"""Convert the given date string to timestamp |
59
|
|
|
|
60
|
|
|
:param date_string: |
61
|
|
|
:return: |
62
|
|
|
""" |
63
|
1 |
|
if re.match(r'\d+.\d+.\d+ \d+:\d+:\d+', date_string): |
64
|
1 |
|
return datetime.datetime.strptime(date_string, "%d.%m.%Y %H:%M:%S").timestamp() |
65
|
1 |
|
elif re.match(r'\d+.\d+.\d+ \d+:\d+', date_string): |
66
|
1 |
|
return datetime.datetime.strptime(date_string, "%d.%m.%Y %H:%M").timestamp() |
67
|
1 |
|
elif re.match(r'\d+.\d+.\d+', date_string): |
68
|
1 |
|
return datetime.datetime.strptime(date_string, "%d.%m.%Y").timestamp() |
69
|
|
|
else: |
70
|
1 |
|
raise AttributeError("The date doesn't fit the format: d.m.Y[ H:M[:S]]") |
71
|
|
|
|
72
|
1 |
|
def apply(self, directory='', file_system_objects=None) -> Generator[str, None, None]: |
73
|
|
|
"""Apply the filter values to the given FSOs(File System Objects) |
74
|
|
|
|
75
|
|
|
:type directory: str |
76
|
|
|
:type file_system_objects: list|tuple|Generator |
77
|
|
|
:return: |
78
|
|
|
""" |
79
|
1 |
|
self._directory = directory |
80
|
1 |
|
self._file_system_objects = file_system_objects |
81
|
|
|
|
82
|
1 |
|
for file_system_object in self.file_system_objects: |
83
|
1 |
|
abs_path = os.path.join(directory, file_system_object) |
84
|
|
|
|
85
|
|
|
# check if the FSO exists, else we can't access the metadata |
86
|
1 |
|
if not os.path.exists(abs_path): |
87
|
1 |
|
continue |
88
|
|
|
|
89
|
|
|
# check if the FSO is a file |
90
|
1 |
|
if self._filter_assert_is_file and not os.path.isfile(abs_path): |
91
|
1 |
|
continue |
92
|
|
|
|
93
|
|
|
# check if the FSO is a folder |
94
|
1 |
|
if self._filter_assert_is_folder and not os.path.isdir(abs_path): |
95
|
1 |
|
continue |
96
|
|
|
|
97
|
1 |
|
file_stats = os.stat(abs_path) |
98
|
|
|
|
99
|
|
|
# check if the FSO creation date matches the constraint |
100
|
1 |
|
if self._filter_creation_date and not self.apply_creation_date(file_stats): |
101
|
1 |
|
continue |
102
|
|
|
|
103
|
|
|
# check if the FSO modification date matches the constraint |
104
|
1 |
|
if self._filter_modified_date and not self.apply_modified_date(file_stats): |
105
|
1 |
|
continue |
106
|
|
|
|
107
|
|
|
# check if the FSO name matches the constraint |
108
|
1 |
|
if self._filter_name and not self._filter_name.cmp_func(file_system_object, self._filter_name.value): |
109
|
1 |
|
continue |
110
|
|
|
|
111
|
|
|
# check if the FSO suffix matches the constraint |
112
|
1 |
|
if self._filter_file_type and not self._filter_file_type.cmp_func(Path(abs_path).suffix, |
113
|
|
|
self._filter_file_type.value): |
114
|
1 |
|
continue |
115
|
|
|
|
116
|
|
|
# check if the FSO size matches the constraint |
117
|
1 |
|
if self._filter_file_size and not self._filter_file_size.cmp_func(file_stats[ST_SIZE], |
118
|
|
|
self._filter_file_size.value): |
119
|
1 |
|
continue |
120
|
|
|
|
121
|
1 |
|
yield file_system_object |
122
|
|
|
|
123
|
1 |
|
def apply_creation_date(self, file_stats: os.stat_result) -> bool: |
124
|
|
|
"""Apply creation date option |
125
|
|
|
|
126
|
|
|
:param file_stats: |
127
|
|
|
:return: |
128
|
|
|
""" |
129
|
1 |
|
creation_time = file_stats[ST_CTIME] |
130
|
1 |
|
expected_creation_time = self._get_timestamp_from_datestring(self._filter_creation_date.value) |
131
|
1 |
|
if self._filter_creation_date.cmp_func(creation_time, expected_creation_time): |
132
|
1 |
|
return True |
133
|
|
|
else: |
134
|
1 |
|
return False |
135
|
|
|
|
136
|
1 |
|
def apply_modified_date(self, file_stats: os.stat_result) -> bool: |
137
|
|
|
"""Apply modified date option |
138
|
|
|
|
139
|
|
|
:param file_stats: |
140
|
|
|
:return: |
141
|
|
|
""" |
142
|
1 |
|
modified_time = file_stats[ST_MTIME] |
143
|
1 |
|
expected_modified_time = self._get_timestamp_from_datestring(self._filter_modified_date.value) |
144
|
1 |
|
if self._filter_modified_date.cmp_func(modified_time, expected_modified_time): |
145
|
1 |
|
return True |
146
|
|
|
else: |
147
|
|
|
return False |
148
|
|
|
|