Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2016 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of xdcc-dl.
6xdcc-dl is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11xdcc-dl is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with xdcc-dl. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20# imports
21import os
22import re
23from xdcc_dl.entities.IrcServer import IrcServer
24from puffotter.units import human_readable_bytes
27class XDCCPack(object):
28 """
29 Class that models an XDCC Pack
30 """
32 def __init__(self, server: IrcServer, bot: str, packnumber: int):
33 """
34 Initializes an XDCC object. It contains all the necessary information
35 for joining the correct IRC server and channel and sending the
36 download request to the correct bot, then storing the
37 received file in the predetermined location.
38 If the destination is a directory, the file will be stored
39 in the directory with the default file name,
40 if not the file will be saved at the destination exactly.
41 The file extension will stay as in the original filename
43 :param server: The Sever to be used by the XDCC Bot
44 :param bot: The bot serving the file
45 :param packnumber: The packnumber of the desired file
46 """
47 self.server = server
48 self.bot = bot
49 self.packnumber = packnumber
50 self.directory = os.getcwd()
51 self.filename = ""
52 self.size = 0
54 self.original_filename = ""
56 def is_filename_valid(self, filename: str) -> bool:
57 """
58 Checks if a filename is the same as the original filename,
59 if one was set previously.
60 This is used internally by the IRC Bot to check if a file that
61 was offered to the bot actually matches the file we want to download.
63 :param filename: The file name to check
64 :return: True, if the names match, or no original filename was
65 set, otherwise False
66 """
67 if self.original_filename != "":
68 return filename == self.original_filename
69 else:
70 return True
72 def set_filename(self, filename: str, override: bool = False):
73 """
74 Sets the filename (or only the file extension) of the target file
76 :param filename: the filename as provided by the XDCC bot
77 :param override: Overrides the current filename
78 :return: None
79 """
80 if self.filename and len(filename.split(".")) > 1 and not override:
81 extension = filename.rsplit(".", 1)[1]
82 if not self.filename.endswith(extension):
83 self.filename += "." + extension
85 if not self.filename or override:
86 self.filename = filename
88 def set_original_filename(self, filename: str):
89 """
90 Sets the 'original' filename,
91 a.k.a the name of the actual file to download.
92 This is a method that should only be used by the pack searchers
93 to add filename checks during the download.
95 :param filename: The original filename as found by the PackSearcher
96 :return: None
97 """
98 self.original_filename = filename
100 def set_directory(self, directory: str):
101 """
102 Sets the target directory of the XDCC PAck
104 :param directory: the target directory
105 :return: None
106 """
107 self.directory = directory
109 def set_size(self, size: int):
110 """
111 Sets the file size of the XDCC pack in Bytes
113 :param size: the size of the pack
114 :return: None
115 """
116 self.size = size
118 def get_server(self) -> IrcServer:
119 """
120 :return: The server
121 """
122 return self.server
124 def get_filepath(self) -> str:
125 """
126 :return: The full destination file path
127 """
128 return os.path.join(self.directory, self.filename)
130 def get_filename(self) -> str:
131 """
132 :return: The currently set filename
133 """
134 return self.filename
136 def get_size(self) -> int:
137 """
138 :return: The currently set file size
139 """
140 return self.size
142 def get_bot(self) -> str:
143 """
144 :return: The bot
145 """
146 return self.bot
148 def get_packnumber(self) -> int:
149 """
150 :return: the pack number
151 """
152 return self.packnumber
154 def get_request_message(self, full: bool = False) -> str:
155 """
156 Generates an xdcc send message to be sent to the bot to initiate
157 the XDCC connection
159 :param full: Returns the entire message string,
160 including the bot's name, as seen on packlist sites
161 :return: The generated message string
162 """
163 if full:
164 return "/msg " + self.bot + " xdcc send #" + str(self.packnumber)
165 else:
166 return "xdcc send #" + str(self.packnumber)
168 def __str__(self) -> str:
169 """
170 :return: A string representation of the pack
171 """
172 return f"{self.filename} (/msg {self.bot} " \
173 f"{self.get_request_message()}) " \
174 f"[{human_readable_bytes(self.size)}]"
176 def __eq__(self, other) -> bool:
177 """
178 Checks two objects for equality
179 :param other: The other object to check against
180 :return: True if the objects are equal, false otherwise
181 """
182 if not issubclass(type(self), type(other)): 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true
183 return False
185 return self.bot == other.bot \
186 and self.packnumber == other.packnumber \
187 and self.server == other.server \
188 and self.filename == other.filename \
189 and self.directory == other.directory
191 @classmethod
192 def from_xdcc_message(cls, xdcc_message: str,
193 destination_directory: str = os.getcwd(),
194 server: str = "irc.rizon.net") \
195 -> list:
196 """
197 Generates XDCC Packs from an xdcc message of the form
198 "/msg <bot> xdcc send #<packnumber>[-<packnumber>]"
200 :param xdcc_message: the XDCC message to parse
201 :param destination_directory: the destination directory of the file
202 :param server: the server to use, defaults to irc.rizon.net for
203 simplicity's sake
204 :return: The generated XDCC Packs in a list
205 """
206 regex = r"^/msg [^ ]+ xdcc send #" \
207 r"[0-9]+((,[0-9]+)*|(-[0-9]+(;[0-9]+)?)?)$"
208 if not re.search(regex, xdcc_message): 208 ↛ 209line 208 didn't jump to line 209, because the condition on line 208 was never true
209 return []
211 bot = xdcc_message.split("/msg ")[1].split(" ")[0]
213 try:
214 packnumber = xdcc_message.rsplit("#", 1)[1]
215 packnumbers = packnumber.split(",")
217 packs = []
218 for number in packnumbers:
219 xdcc_pack = XDCCPack(IrcServer(server), bot, int(number))
220 xdcc_pack.set_directory(destination_directory)
221 packs.append(xdcc_pack)
223 return packs
225 except ValueError:
226 start, end = xdcc_message.rsplit("#", 1)[1].split("-")
228 try:
229 step = int(end.split(";")[1])
230 end = end.split(";")[0]
231 except (IndexError, ValueError):
232 step = 1
234 packs = []
235 for pack in range(int(start), int(end) + 1, step):
236 xdcc_pack = XDCCPack(IrcServer(server), bot, pack)
237 xdcc_pack.set_directory(destination_directory)
238 packs.append(xdcc_pack)
239 return packs