Hide keyboard shortcuts

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> 

3 

4This file is part of xdcc-dl. 

5 

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. 

10 

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. 

15 

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""" 

19 

20# imports 

21import os 

22import re 

23from xdcc_dl.entities.IrcServer import IrcServer 

24from puffotter.units import human_readable_bytes 

25 

26 

27class XDCCPack(object): 

28 """ 

29 Class that models an XDCC Pack 

30 """ 

31 

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 

42 

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 

53 

54 self.original_filename = "" 

55 

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. 

62 

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 

71 

72 def set_filename(self, filename: str, override: bool = False): 

73 """ 

74 Sets the filename (or only the file extension) of the target file 

75 

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 

84 

85 if not self.filename or override: 

86 self.filename = filename 

87 

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. 

94 

95 :param filename: The original filename as found by the PackSearcher 

96 :return: None 

97 """ 

98 self.original_filename = filename 

99 

100 def set_directory(self, directory: str): 

101 """ 

102 Sets the target directory of the XDCC PAck 

103 

104 :param directory: the target directory 

105 :return: None 

106 """ 

107 self.directory = directory 

108 

109 def set_size(self, size: int): 

110 """ 

111 Sets the file size of the XDCC pack in Bytes 

112 

113 :param size: the size of the pack 

114 :return: None 

115 """ 

116 self.size = size 

117 

118 def get_server(self) -> IrcServer: 

119 """ 

120 :return: The server 

121 """ 

122 return self.server 

123 

124 def get_filepath(self) -> str: 

125 """ 

126 :return: The full destination file path 

127 """ 

128 return os.path.join(self.directory, self.filename) 

129 

130 def get_filename(self) -> str: 

131 """ 

132 :return: The currently set filename 

133 """ 

134 return self.filename 

135 

136 def get_size(self) -> int: 

137 """ 

138 :return: The currently set file size 

139 """ 

140 return self.size 

141 

142 def get_bot(self) -> str: 

143 """ 

144 :return: The bot 

145 """ 

146 return self.bot 

147 

148 def get_packnumber(self) -> int: 

149 """ 

150 :return: the pack number 

151 """ 

152 return self.packnumber 

153 

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 

158 

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) 

167 

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)}]" 

175 

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 

184 

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 

190 

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>]" 

199 

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 [] 

210 

211 bot = xdcc_message.split("/msg ")[1].split(" ")[0] 

212 

213 try: 

214 packnumber = xdcc_message.rsplit("#", 1)[1] 

215 packnumbers = packnumber.split(",") 

216 

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) 

222 

223 return packs 

224 

225 except ValueError: 

226 start, end = xdcc_message.rsplit("#", 1)[1].split("-") 

227 

228 try: 

229 step = int(end.split(";")[1]) 

230 end = end.split(";")[0] 

231 except (IndexError, ValueError): 

232 step = 1 

233 

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