Differential 0.6.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. differential/__init__.py +0 -0
  2. differential/base_plugin.py +687 -0
  3. differential/commands.py +29 -0
  4. differential/constants.py +63 -0
  5. differential/main.py +47 -0
  6. differential/plugin_loader.py +49 -0
  7. differential/plugin_register.py +44 -0
  8. differential/plugins/__init__.py +0 -0
  9. differential/plugins/chdbits.py +17 -0
  10. differential/plugins/chdbits_encode.py +210 -0
  11. differential/plugins/gazelle.py +18 -0
  12. differential/plugins/greatposterwall.py +21 -0
  13. differential/plugins/hdbits.py +39 -0
  14. differential/plugins/league_official.py +134 -0
  15. differential/plugins/lemonhd.py +19 -0
  16. differential/plugins/nexusphp.py +18 -0
  17. differential/plugins/pterclub.py +21 -0
  18. differential/plugins/ptp.py +20 -0
  19. differential/plugins/unit3d.py +18 -0
  20. differential/tools/BDinfoCli.0.7.3/BDInfo.exe +0 -0
  21. differential/tools/BDinfoCli.0.7.3/DiscUtils.Common.dll +0 -0
  22. differential/tools/BDinfoCli.0.7.3/DiscUtils.dll +0 -0
  23. differential/tools/BDinfoCli.0.7.3/Microsoft.WindowsAPICodePack.Shell.dll +0 -0
  24. differential/tools/BDinfoCli.0.7.3/Microsoft.WindowsAPICodePack.ShellExtensions.dll +0 -0
  25. differential/tools/BDinfoCli.0.7.3/Microsoft.WindowsAPICodePack.dll +0 -0
  26. differential/tools/BDinfoCli.0.7.3/ZedGraph.dll +0 -0
  27. differential/tools/__init__.py +0 -0
  28. differential/torrent.py +134 -0
  29. differential/utils/__init__.py +0 -0
  30. differential/utils/binary.py +106 -0
  31. differential/utils/browser.py +23 -0
  32. differential/utils/config.py +51 -0
  33. differential/utils/image/__init__.py +19 -0
  34. differential/utils/image/byr.py +68 -0
  35. differential/utils/image/chevereto.py +139 -0
  36. differential/utils/image/cloudinary.py +57 -0
  37. differential/utils/image/hdbits.py +105 -0
  38. differential/utils/image/imgbox.py +135 -0
  39. differential/utils/image/imgurl.py +51 -0
  40. differential/utils/image/ptpimg.py +39 -0
  41. differential/utils/image/smms.py +49 -0
  42. differential/utils/image/types.py +30 -0
  43. differential/utils/mediainfo.py +211 -0
  44. differential/utils/mediainfo_handler.py +190 -0
  45. differential/utils/nfo.py +12 -0
  46. differential/utils/parse.py +30 -0
  47. differential/utils/ptgen/bangumi.py +71 -0
  48. differential/utils/ptgen/base.py +50 -0
  49. differential/utils/ptgen/douban.py +137 -0
  50. differential/utils/ptgen/epic.py +50 -0
  51. differential/utils/ptgen/imdb.py +124 -0
  52. differential/utils/ptgen/indienova.py +72 -0
  53. differential/utils/ptgen/parser.py +33 -0
  54. differential/utils/ptgen/steam.py +54 -0
  55. differential/utils/ptgen_handler.py +83 -0
  56. differential/utils/screenshot_handler.py +180 -0
  57. differential/utils/torrent.py +69 -0
  58. differential/utils/uploader/__init__.py +2 -0
  59. differential/utils/uploader/auto_feed.py +134 -0
  60. differential/utils/uploader/easy_upload.py +65 -0
  61. differential/version.py +1 -0
  62. differential-0.6.5.1.dist-info/METADATA +134 -0
  63. differential-0.6.5.1.dist-info/RECORD +66 -0
  64. differential-0.6.5.1.dist-info/WHEEL +4 -0
  65. differential-0.6.5.1.dist-info/entry_points.txt +6 -0
  66. differential-0.6.5.1.dist-info/licenses/LICENSE +21 -0
File without changes
@@ -0,0 +1,687 @@
1
+ import re
2
+ import json
3
+ import argparse
4
+ from pathlib import Path
5
+ from typing import Optional
6
+ from itertools import chain
7
+ from urllib.parse import quote
8
+ from abc import ABC, abstractmethod
9
+
10
+ from loguru import logger
11
+
12
+ from differential.plugin_register import PluginRegister
13
+ from differential.torrent import TorrnetBase
14
+ from differential.constants import ImageHosting
15
+ from differential.utils.browser import open_link
16
+ from differential.utils.torrent import make_torrent
17
+ from differential.utils.parse import parse_encoder_log
18
+ from differential.utils.uploader import EasyUpload, AutoFeed
19
+ from differential.utils.mediainfo_handler import MediaInfoHandler
20
+ from differential.utils.ptgen_handler import PTGenHandler
21
+ from differential.utils.screenshot_handler import ScreenshotHandler
22
+ from differential.utils.nfo import generate_nfo
23
+ from differential.utils.ptgen.base import PTGenData, DataType
24
+ from differential.utils.ptgen.imdb import IMDBData
25
+ from differential.utils.ptgen.douban import DoubanData
26
+
27
+
28
+ class Base(ABC, TorrnetBase, metaclass=PluginRegister):
29
+ @classmethod
30
+ @abstractmethod
31
+ def add_parser(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
32
+ parser.add_argument(
33
+ "-c",
34
+ "--config",
35
+ type=str,
36
+ help="配置文件的路径,默认为config.ini",
37
+ default="config.ini",
38
+ )
39
+ parser.add_argument(
40
+ "-l", "--log", type=str, help="log文件的路径", default=argparse.SUPPRESS
41
+ )
42
+ parser.add_argument(
43
+ "-f",
44
+ "--folder",
45
+ type=str,
46
+ help="种子文件夹的路径",
47
+ default=argparse.SUPPRESS,
48
+ )
49
+ parser.add_argument(
50
+ "-u", "--url", type=str, help="豆瓣链接", default=argparse.SUPPRESS
51
+ )
52
+ parser.add_argument(
53
+ "-uu",
54
+ "--upload-url",
55
+ type=str,
56
+ help="PT站点上传的路径,一般为https://xxxxx.com/upload.php",
57
+ default=argparse.SUPPRESS,
58
+ )
59
+ parser.add_argument(
60
+ "-t",
61
+ "--make-torrent",
62
+ action="store_true",
63
+ help="是否制种,默认否",
64
+ default=argparse.SUPPRESS,
65
+ )
66
+ parser.add_argument(
67
+ "-n",
68
+ "--generate-nfo",
69
+ action="store_true",
70
+ help="是否用MediaInfo生成NFO文件,默认否",
71
+ default=argparse.SUPPRESS,
72
+ )
73
+ parser.add_argument(
74
+ "-s",
75
+ "--screenshot-count",
76
+ type=int,
77
+ help="截图数量,默认为0,即不生成截图",
78
+ default=argparse.SUPPRESS,
79
+ )
80
+ parser.add_argument(
81
+ "--screenshot-path",
82
+ type=str,
83
+ help="截图文件夹,会在提供的文件夹中查找图片并上传,不会再生成截图",
84
+ default=argparse.SUPPRESS,
85
+ )
86
+ parser.add_argument(
87
+ "--create-folder",
88
+ action="store_true",
89
+ dest="create_folder",
90
+ help="如果目标为文件,创建文件夹并把目标文件放入其中",
91
+ default=argparse.SUPPRESS,
92
+ )
93
+ parser.add_argument(
94
+ "--use-short-bdinfo",
95
+ action="store_true",
96
+ help="使用QUICK SUMMARY作为BDInfo,默认使用完整BDInfo",
97
+ default=argparse.SUPPRESS,
98
+ )
99
+ parser.add_argument(
100
+ "--no-scan-bdinfo",
101
+ action="store_false",
102
+ dest="scan_bdinfo",
103
+ help="如果为原盘,跳过扫描BDInfo",
104
+ default=argparse.SUPPRESS,
105
+ )
106
+ parser.add_argument(
107
+ "--optimize-screenshot",
108
+ action="store_true",
109
+ help="是否压缩截图(无损),默认压缩",
110
+ default=argparse.SUPPRESS,
111
+ )
112
+ parser.add_argument(
113
+ "--image-hosting",
114
+ type=ImageHosting,
115
+ help=f"图床的类型,现在支持{','.join(i.value for i in ImageHosting)}",
116
+ default=argparse.SUPPRESS,
117
+ )
118
+ parser.add_argument(
119
+ "--ptpimg-api-key",
120
+ type=str,
121
+ help="PTPIMG的API Key",
122
+ default=argparse.SUPPRESS,
123
+ )
124
+ parser.add_argument(
125
+ "--hdbits-cookie",
126
+ type=str,
127
+ help="HDBits的Cookie",
128
+ default=argparse.SUPPRESS,
129
+ )
130
+ parser.add_argument(
131
+ "--hdbits-thumb-size",
132
+ type=str,
133
+ help="HDBits图床缩略图的大小,默认w300",
134
+ default=argparse.SUPPRESS,
135
+ )
136
+ parser.add_argument(
137
+ "--chevereto-hosting-url",
138
+ type=str,
139
+ help="自建chevereto图床的地址",
140
+ default=argparse.SUPPRESS,
141
+ )
142
+ parser.add_argument(
143
+ "--chevereto-api-key",
144
+ type=str,
145
+ help="自建Chevereto的API Key,详情见https://v3-docs.chevereto.com/api/#api-call",
146
+ default=argparse.SUPPRESS,
147
+ )
148
+ parser.add_argument(
149
+ "--chevereto-username",
150
+ type=str,
151
+ help="如果自建Chevereto的API未开放,请设置username和password",
152
+ default=argparse.SUPPRESS,
153
+ )
154
+ parser.add_argument(
155
+ "--chevereto-password",
156
+ type=str,
157
+ help="如果自建Chevereto的API未开放,请设置username和password",
158
+ default=argparse.SUPPRESS,
159
+ )
160
+ parser.add_argument(
161
+ "--imgurl-api-key",
162
+ type=str,
163
+ help="Imgurl的API Key",
164
+ default=argparse.SUPPRESS,
165
+ )
166
+ parser.add_argument(
167
+ "--smms-api-key", type=str, help="SM.MS的API Key", default=argparse.SUPPRESS
168
+ )
169
+ parser.add_argument(
170
+ "--byr-cookie",
171
+ type=str,
172
+ help="BYR的Cookie,可登录后访问任意页面F12查看",
173
+ default=argparse.SUPPRESS,
174
+ )
175
+ parser.add_argument(
176
+ "--byr-alternative-url",
177
+ type=str,
178
+ help="BYR反代地址(如有),可为空",
179
+ default=argparse.SUPPRESS,
180
+ )
181
+ parser.add_argument(
182
+ "--cloudinary-cloud-name",
183
+ type=str,
184
+ help="Cloudinary的cloud name",
185
+ default=argparse.SUPPRESS,
186
+ )
187
+ parser.add_argument(
188
+ "--cloudinary-api-key",
189
+ type=str,
190
+ help="Cloudinary的api key",
191
+ default=argparse.SUPPRESS,
192
+ )
193
+ parser.add_argument(
194
+ "--cloudinary-api-secret",
195
+ type=str,
196
+ help="Cloudinary的api secret",
197
+ default=argparse.SUPPRESS,
198
+ )
199
+ parser.add_argument(
200
+ "--imgbox-username",
201
+ type=str,
202
+ help="Imgbox图床登录用户名,留空则匿名上传",
203
+ default=argparse.SUPPRESS,
204
+ )
205
+ parser.add_argument(
206
+ "--imgbox-password",
207
+ type=str,
208
+ help="Imgbox图床登录密码,留空则匿名上传",
209
+ default=argparse.SUPPRESS,
210
+ )
211
+ parser.add_argument(
212
+ "--imgbox-thumbnail-size",
213
+ type=str,
214
+ help="Imgbox图床缩略图的大小,默认300r",
215
+ default=argparse.SUPPRESS,
216
+ )
217
+ parser.add_argument(
218
+ "--imgbox-family-safe",
219
+ action="store_true",
220
+ dest="imgbox_family_safe",
221
+ help="Imgbox图床是否是非成人内容",
222
+ default=argparse.SUPPRESS,
223
+ )
224
+ parser.add_argument(
225
+ "--imgbox-not-family-safe",
226
+ action="store_false",
227
+ dest="imgbox_family_safe",
228
+ help="Imgbox图床是否是成人内容",
229
+ default=argparse.SUPPRESS,
230
+ )
231
+ parser.add_argument(
232
+ "--ptgen-url", type=str, help="自定义PTGEN的地址", default=argparse.SUPPRESS
233
+ )
234
+ parser.add_argument(
235
+ "--ptgen-retry",
236
+ type=int,
237
+ help="PTGEN重试次数,默认为3次",
238
+ default=argparse.SUPPRESS,
239
+ )
240
+ parser.add_argument(
241
+ "--announce-url",
242
+ type=str,
243
+ help="制种时announce地址",
244
+ default=argparse.SUPPRESS,
245
+ )
246
+
247
+ parser.add_argument(
248
+ "--encoder-log", type=str, help="压制log的路径", default=argparse.SUPPRESS
249
+ )
250
+ upload_option_group = parser.add_mutually_exclusive_group()
251
+ upload_option_group.add_argument(
252
+ "--easy-upload",
253
+ action="store_true",
254
+ help="使用树大Easy Upload插件自动填充",
255
+ dest="easy_upload",
256
+ default=argparse.SUPPRESS,
257
+ )
258
+ upload_option_group.add_argument(
259
+ "--auto-feed",
260
+ action="store_true",
261
+ help="使用明日大Auto Feed插件自动填充",
262
+ dest="auto_feed",
263
+ default=argparse.SUPPRESS,
264
+ )
265
+ parser.add_argument(
266
+ "--trim-description",
267
+ action="store_true",
268
+ help="是否在生成的链接中省略种子描述,该选项主要是为了解决浏览器限制URL长度的问题,默认关闭",
269
+ default=argparse.SUPPRESS,
270
+ )
271
+ parser.add_argument(
272
+ "--use-short-url",
273
+ action="store_true",
274
+ help="是否缩短生成的上传链接",
275
+ default=argparse.SUPPRESS,
276
+ )
277
+ parser.add_argument(
278
+ "--no-reuse-torrent",
279
+ action="store_false",
280
+ dest="reuse_torrent",
281
+ help="是否直接在差速器已经制作的种子基础上重新制种",
282
+ default=argparse.SUPPRESS,
283
+ )
284
+ parser.add_argument(
285
+ "--from-torrent",
286
+ type=str,
287
+ help="提供种子,在此基础上,直接洗种生成新种子",
288
+ default=argparse.SUPPRESS,
289
+ )
290
+ return parser
291
+
292
+ def __init__(
293
+ self,
294
+ folder: str,
295
+ url: str,
296
+ upload_url: str,
297
+ screenshot_count: int = 0,
298
+ screenshot_path: str = None,
299
+ optimize_screenshot: bool = True,
300
+ create_folder: bool = False,
301
+ use_short_bdinfo: bool = False,
302
+ scan_bdinfo: bool = True,
303
+ image_hosting: ImageHosting = ImageHosting.PTPIMG,
304
+ chevereto_hosting_url: str = "",
305
+ imgurl_hosting_url: str = "",
306
+ ptpimg_api_key: str = None,
307
+ hdbits_cookie: str = None,
308
+ hdbits_thumb_size: str = "w300",
309
+ chevereto_api_key: str = None,
310
+ chevereto_username: str = None,
311
+ chevereto_password: str = None,
312
+ cloudinary_cloud_name: str = None,
313
+ cloudinary_api_key: str = None,
314
+ cloudinary_api_secret: str = None,
315
+ imgurl_api_key: str = None,
316
+ smms_api_key: str = None,
317
+ byr_cookie: str = None,
318
+ byr_alternative_url: str = None,
319
+ imgbox_username: str = None,
320
+ imgbox_password: str = None,
321
+ imgbox_thumbnail_size: str = "300r",
322
+ imgbox_family_safe: bool = True,
323
+ ptgen_url: str = "https://ptgen.lgto.workers.dev",
324
+ second_ptgen_url: str = "https://api.slyw.me",
325
+ announce_url: str = "https://example.com",
326
+ ptgen_retry: int = 3,
327
+ generate_nfo: bool = False,
328
+ make_torrent: bool = False,
329
+ easy_upload: bool = False,
330
+ auto_feed: bool = False,
331
+ trim_description: bool = False,
332
+ use_short_url: bool = False,
333
+ encoder_log: str = "",
334
+ reuse_torrent: bool = True,
335
+ from_torrent: str = None,
336
+ **kwargs,
337
+ ):
338
+ self.folder = Path(folder)
339
+ self.url = url
340
+ self.upload_url = upload_url
341
+
342
+ self.announce_url = announce_url
343
+ self.generate_nfo = generate_nfo
344
+ self.make_torrent = make_torrent
345
+ self.easy_upload = easy_upload
346
+ self.auto_feed = auto_feed
347
+ self.trim_description = trim_description
348
+ self.use_short_url = use_short_url
349
+ self.encoder_log = encoder_log
350
+ self.reuse_torrent = reuse_torrent
351
+ self.from_torrent = from_torrent
352
+
353
+ self.mediainfo_handler = MediaInfoHandler(
354
+ folder=self.folder,
355
+ create_folder=create_folder,
356
+ use_short_bdinfo=use_short_bdinfo,
357
+ scan_bdinfo=scan_bdinfo,
358
+ )
359
+ self.ptgen_handler = PTGenHandler(
360
+ url=self.url,
361
+ ptgen_url=ptgen_url,
362
+ second_ptgen_url=second_ptgen_url,
363
+ ptgen_retry=ptgen_retry,
364
+ )
365
+ self.screenshot_handler = ScreenshotHandler(
366
+ folder=self.folder,
367
+ screenshot_count=screenshot_count,
368
+ screenshot_path=screenshot_path,
369
+ optimize_screenshot=optimize_screenshot,
370
+ image_hosting=image_hosting,
371
+ chevereto_hosting_url=chevereto_hosting_url,
372
+ imgurl_hosting_url=imgurl_hosting_url,
373
+ ptpimg_api_key=ptpimg_api_key,
374
+ hdbits_cookie=hdbits_cookie,
375
+ hdbits_thumb_size=hdbits_thumb_size,
376
+ chevereto_username=chevereto_username,
377
+ chevereto_password=chevereto_password,
378
+ chevereto_api_key=chevereto_api_key,
379
+ cloudinary_cloud_name=cloudinary_cloud_name,
380
+ cloudinary_api_key=cloudinary_api_key,
381
+ cloudinary_api_secret=cloudinary_api_secret,
382
+ imgurl_api_key=imgurl_api_key,
383
+ smms_api_key=smms_api_key,
384
+ byr_cookie=byr_cookie,
385
+ byr_alternative_url=byr_alternative_url,
386
+ imgbox_username=imgbox_username,
387
+ imgbox_password=imgbox_password,
388
+ imgbox_thumbnail_size=imgbox_thumbnail_size,
389
+ imgbox_family_safe=imgbox_family_safe,
390
+ )
391
+
392
+ self.main_file: Optional[Path] = None
393
+ self.ptgen: Optional[PTGenData] = None
394
+ self.douban: Optional[DoubanData] = None
395
+ self.imdb: Optional[IMDBData] = None
396
+
397
+ def upload(self):
398
+ self._prepare()
399
+ if self.easy_upload:
400
+ torrent_info = self.easy_upload_torrent_info
401
+ if self.trim_description:
402
+ # 直接打印简介部分来绕过浏览器的链接长度限制
403
+ torrent_info["description"] = ""
404
+ logger.trace(f"torrent_info: {torrent_info}")
405
+ link = f"{self.upload_url}#torrentInfo={quote(json.dumps(torrent_info))}"
406
+ logger.trace(f"已生成自动上传链接:{link}")
407
+ if self.trim_description:
408
+ logger.info(f"种子描述:\n{self.description}")
409
+ open_link(link, self.use_short_url)
410
+ elif self.auto_feed:
411
+ link = f"{self.upload_url}#{self.auto_feed_info}"
412
+ # if self.trim_description:
413
+ # logger.info(f"种子描述:\n{self.description}")
414
+ logger.trace(f"已生成自动上传链接:{link}")
415
+ open_link(link, self.use_short_url)
416
+ else:
417
+ logger.info(
418
+ "\n"
419
+ f"标题: {self.title}\n"
420
+ f"副标题: {self.subtitle}\n"
421
+ f"豆瓣: {self.douban_url}\n"
422
+ f"IMDB: {self.imdb_url}\n"
423
+ f"视频编码: {self.video_codec} 音频编码: {self.audio_codec} 分辨率: {self.resolution}\n"
424
+ f"描述:\n{self.description}"
425
+ )
426
+
427
+ def _prepare(self):
428
+ self.main_file = self.mediainfo_handler.find_mediainfo()
429
+ self.ptgen, self.douban, self.imdb = self.ptgen_handler.fetch_ptgen_info()
430
+ self.screenshot_handler.collect_screenshots(
431
+ self.main_file,
432
+ self.mediainfo_handler.resolution,
433
+ self.mediainfo_handler.duration,
434
+ )
435
+
436
+ if self.generate_nfo:
437
+ generate_nfo(self.folder, self.mediainfo_handler.media_info)
438
+
439
+ if self.make_torrent:
440
+ make_torrent(
441
+ self.folder,
442
+ self.announce_url,
443
+ self.__class__.__name__,
444
+ self.reuse_torrent,
445
+ self.from_torrent,
446
+ )
447
+
448
+ @property
449
+ def parsed_encoder_log(self):
450
+ return parse_encoder_log(self.encoder_log)
451
+
452
+ @property
453
+ def title(self):
454
+ # TODO: Either use file name or generate from mediainfo and ptgen
455
+ temp_name = (
456
+ self.folder.name if self.folder.is_dir() else self.folder.stem
457
+ ).replace(".", " ")
458
+ temp_name = re.sub(r"(?<=5|7)( )1(?=.*$)", ".1", temp_name)
459
+ return temp_name
460
+
461
+ @property
462
+ def subtitle(self):
463
+ if not self.douban:
464
+ return self.ptgen.subtitle
465
+ return self.douban.subtitle
466
+
467
+ @property
468
+ def media_info(self):
469
+ return self.mediainfo_handler.media_info
470
+
471
+ @property
472
+ def media_infos(self):
473
+ return []
474
+
475
+ @property
476
+ def description(self):
477
+ return "{}\n\n[quote]{}{}[/quote]\n\n{}".format(
478
+ self.ptgen.format,
479
+ self.media_info,
480
+ "\n\n" + self.parsed_encoder_log if self.parsed_encoder_log else "",
481
+ "\n".join(
482
+ [f"{uploaded}" for uploaded in self.screenshot_handler.screenshots]
483
+ ),
484
+ )
485
+
486
+ @property
487
+ def original_description(self):
488
+ return ""
489
+
490
+ @property
491
+ def douban_url(self):
492
+ if self.douban:
493
+ return f"https://movie.douban.com/subject/{self.douban.sid}"
494
+ return ""
495
+
496
+ @property
497
+ def douban_info(self):
498
+ return ""
499
+
500
+ @property
501
+ def imdb_url(self):
502
+ return getattr(self.ptgen, "imdb_link", "")
503
+
504
+ @property
505
+ def screenshots(self):
506
+ return [u.url for u in self.screenshot_handler.screenshots]
507
+
508
+ @property
509
+ def poster(self):
510
+ return getattr(self.ptgen, "poster")
511
+
512
+ @property
513
+ def year(self):
514
+ return (
515
+ self.imdb.year
516
+ if self.imdb
517
+ else self.douban.year if self.douban else getattr(self.ptgen, "year", "")
518
+ )
519
+
520
+ @property
521
+ def category(self):
522
+ if self.douban:
523
+ if "演唱会" in self.douban.genre and "音乐" in self.douban.genre:
524
+ return "concert"
525
+ if self.imdb:
526
+ if "Documentary" in self.imdb.genre:
527
+ return "documentary"
528
+ if self.imdb.type_ == DataType.MOVIE:
529
+ return "movie"
530
+ if self.imdb.type_ == DataType.TV_SERIES:
531
+ return "tvPack"
532
+ return self.ptgen.type_
533
+
534
+ @property
535
+ def video_type(self):
536
+ if "webdl" in self.folder.name.lower() or "web-dl" in self.folder.name.lower():
537
+ return "web"
538
+ elif "remux" in self.folder.name.lower():
539
+ return "remux"
540
+ elif "hdtv" in self.folder.name.lower():
541
+ return "hdtv"
542
+ elif any(e in self.folder.name.lower() for e in ("x264", "x265")):
543
+ return "encode"
544
+ elif "bluray" in self.folder.name.lower() and not any(
545
+ e in self.folder.name.lower() for e in ("x264", "x265")
546
+ ):
547
+ return "bluray"
548
+ elif "uhd" in self.folder.name.lower():
549
+ return "uhdbluray"
550
+ for track in self.mediainfo_handler.tracks:
551
+ if track.track_type == "Video":
552
+ if track.encoding_settings:
553
+ return "encode"
554
+ return ""
555
+
556
+ @property
557
+ def format(self):
558
+ # TODO: Maybe read from mediainfo
559
+ return self.main_file.suffix
560
+
561
+ @property
562
+ def source(self):
563
+ return ""
564
+
565
+ @property
566
+ def video_codec(self):
567
+ for track in self.mediainfo_handler.mediainfo.tracks:
568
+ if track.track_type == "Video":
569
+ if track.encoded_library_name:
570
+ return track.encoded_library_name
571
+ if track.commercial_name == "AVC":
572
+ return "h264"
573
+ if track.commercial_name == "HEVC":
574
+ return "hevc"
575
+ # h264: "AVC/H.264",
576
+ # hevc: "HEVC",
577
+ # x264: "x264",
578
+ # x265: "x265",
579
+ # h265: "HEVC",
580
+ # mpeg2: "MPEG-2",
581
+ # mpeg4: "AVC/H.264",
582
+ # vc1: "VC-1",
583
+ # dvd: "MPEG"
584
+ return ""
585
+
586
+ @property
587
+ def audio_codec(self):
588
+ codec_map = {
589
+ "AAC": "aac",
590
+ "Dolby Digital Plus": "dd+",
591
+ "Dolby Digital": "dd",
592
+ "DTS-HD Master Audio": "dtshdma",
593
+ "Dolby Digital Plus with Dolby Atmos": "atmos",
594
+ "Dolby TrueHD": "truehd",
595
+ "Dolby TrueHD with Dolby Atmos": "truehd",
596
+ }
597
+ for track in self.mediainfo_handler.mediainfo.tracks:
598
+ if track.track_type == "Audio":
599
+ if track.format_info == "Audio Coding 3":
600
+ return "ac3"
601
+ if track.format_info == "Free Lossless Audio Codec":
602
+ return "flac"
603
+ if track.commercial_name in codec_map:
604
+ return codec_map.get(track.commercial_name)
605
+ # TODO: other formats
606
+ # dts: "3",
607
+ # lpcm: "21",
608
+ # dtsx: "3",
609
+ # ape: "2",
610
+ # wav: "22",
611
+ # mp3: "4",
612
+ # m4a: "5",
613
+ # other: "7"
614
+ return ""
615
+
616
+ @property
617
+ def resolution(self):
618
+ for track in self.mediainfo_handler.mediainfo.tracks:
619
+ if track.track_type == "Video":
620
+ if track.height <= 480:
621
+ return "480p"
622
+ elif track.height <= 576:
623
+ return "576p"
624
+ elif track.height <= 720:
625
+ return "720p"
626
+ elif track.height <= 1080:
627
+ if getattr(track, "scan_type__store_method") == "InterleavedFields":
628
+ return "1080i"
629
+ return "1080p"
630
+ elif track.height <= 2160:
631
+ return "2160p"
632
+ elif track.height <= 4320:
633
+ return "4320p"
634
+ return ""
635
+
636
+ @property
637
+ def area(self):
638
+ if self.douban:
639
+ return self.douban.area
640
+ return ""
641
+
642
+ @property
643
+ def movie_name(self):
644
+ if self.imdb:
645
+ return self.imdb.name
646
+ if self.douban:
647
+ return next(iter(self.douban.aka), "")
648
+ return ""
649
+
650
+ @property
651
+ def movie_aka_name(self):
652
+ return ""
653
+
654
+ @property
655
+ def size(self):
656
+ for track in self.mediainfo_handler.tracks:
657
+ if track.track_type == "General":
658
+ return track.file_size
659
+ return ""
660
+
661
+ @property
662
+ def tags(self):
663
+ tags = {}
664
+ for track in self.mediainfo_handler.tracks:
665
+ if track.track_type == "General":
666
+ if track.audio_language_list and "Chinese" in track.audio_language_list:
667
+ tags["chinese_audio"] = True
668
+ if track.text_language_list and "Chinese" in track.text_language_list:
669
+ tags["chinese_subtitle"] = True
670
+ # TODO: hdr, hdr10_plus, dolby_vision, diy, cantonese_audio, false,dts_x, dolby_atoms
671
+ return tags
672
+
673
+ @property
674
+ def other_tags(self):
675
+ return []
676
+
677
+ @property
678
+ def comparisons(self):
679
+ return []
680
+
681
+ @property
682
+ def easy_upload_torrent_info(self):
683
+ return EasyUpload(plugin=self).torrent_info
684
+
685
+ @property
686
+ def auto_feed_info(self):
687
+ return AutoFeed(plugin=self).info