ttttdiva commited on
Commit
6318712
·
verified ·
1 Parent(s): a2567f3

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +54 -99
main.py CHANGED
@@ -104,9 +104,8 @@ class CivitAICrawler:
104
 
105
  def encrypt_with_rclone(self, local_path: str):
106
  """
107
- local_path がファイルかフォルダかで処理を分け、
108
- - フォルダ → 従来通り rclone copy
109
- - ファイル → rclone copyto を使って暗号化
110
  """
111
  if not os.path.exists(local_path):
112
  raise FileNotFoundError(f"[ERROR] Local path not found: {local_path}")
@@ -147,8 +146,16 @@ class CivitAICrawler:
147
  f"[ERROR] {self.config.ENCRYPTED_DIR} not found. Check rclone config."
148
  )
149
 
150
- # 例: upload_encrypted_files の中の再試行処理
151
  def upload_encrypted_files(self, repo_id: str, base_path_in_repo: str = ""):
 
 
 
 
 
 
 
 
 
152
  max_retries = 5
153
  for root, dirs, files in os.walk(self.config.ENCRYPTED_DIR):
154
  for fn in files:
@@ -174,30 +181,21 @@ class CivitAICrawler:
174
  attempt += 1
175
  error_message = str(e)
176
 
177
- # ================================
178
- # 429によるrate-limit検出追加
179
- # ================================
180
- # "You have been rate-limited; you can retry this action in 31 minutes."
181
- # のようなメッセージから時間を抽出し、その時間+1分だけ待機後、再試行
182
  if "rate-limited" in error_message and "minutes" in error_message:
183
- import re
184
  match = re.search(r"in (\d+) minutes?", error_message)
185
  if match:
186
  minutes = int(match.group(1))
187
- # +1分して待機
188
  minutes += 1
189
  logger.warning(f"Rate-limited. Waiting {minutes} minutes before retry...")
190
  time.sleep(minutes * 60)
191
- attempt -= 1 # 同じ attempt カウントで再試行
192
  continue
193
 
194
- # ================================
195
- # すでにある1時間待機処理
196
- # ================================
197
  if "you can retry this action in about 1 hour" in error_message:
198
  logger.warning("Encountered 'retry in 1 hour' error. Waiting 1 hour before retrying...")
199
  time.sleep(3600)
200
- attempt -= 1 # 再試行回数を増やさずにループを続ける
201
  continue
202
 
203
  if "over the limit of 100000 files" in error_message:
@@ -255,8 +253,7 @@ class CivitAICrawler:
255
  def download_model(self, model_versions: list, folder: str):
256
  """
257
  - 最新バージョンのファイルを「folder」へまとめてダウンロード
258
- - 古いバージョンはまだダウンロードしない (実は後から個別にダウンロード)
259
- (※今回は「まとめて old_versions フォルダに入れずに、1ファイルずつ別のメソッドで対処する」流れに)
260
  """
261
  latest_version = model_versions[0]
262
  latest_files = latest_version["files"]
@@ -295,12 +292,12 @@ class CivitAICrawler:
295
  def download_old_version_file_and_upload(self, file_info, parent_folder: str, encrypted_top_name: str):
296
  """
297
  古いバージョンのファイルを1つダウンロード→暗号化アップロード→削除。
298
- old_versions」というフォルダ名をそのまま使う。
299
  """
300
  file_name = file_info["name"]
301
  download_url = file_info["downloadUrl"]
302
 
303
- # old_versions フォルダを作成
304
  old_versions_folder = os.path.join(parent_folder, "old_versions")
305
  os.makedirs(old_versions_folder, exist_ok=True)
306
 
@@ -309,7 +306,6 @@ class CivitAICrawler:
309
  login_detected_count = 0
310
  while login_detected_count < 5:
311
  try:
312
- # old_versionsフォルダへダウンロード
313
  self.download_file(download_url, old_versions_folder, file_name)
314
  except Exception as e:
315
  logger.error(f"Exception while downloading old file {file_name}: {e}")
@@ -326,7 +322,7 @@ class CivitAICrawler:
326
  break
327
 
328
  if login_detected_count >= 5:
329
- # 5回失敗
330
  dummy_file_name = f"{file_name}.download_failed"
331
  dummy_file_path = os.path.join(old_versions_folder, dummy_file_name)
332
  try:
@@ -337,12 +333,12 @@ class CivitAICrawler:
337
  logger.error(f"Failed to create dummy file for old version {file_name}: {e}")
338
  return
339
 
340
- # ダウンロード成功 1ファイルだけ暗号化&アップロード → ローカル削除
341
  try:
342
- # 1) ファイル単位で暗号化するなら
343
  self.encrypt_with_rclone(local_path)
344
 
345
- # 2) 暗号化ファイルをアップロード
346
  self.upload_encrypted_files(
347
  repo_id=self.repo_ids["current"],
348
  base_path_in_repo=f"{encrypted_top_name}/old_versions"
@@ -352,7 +348,7 @@ class CivitAICrawler:
352
  except Exception as e:
353
  logger.error(f"Error uploading old version file {file_name}: {e}")
354
 
355
- # 3) アップロード後、平文ファイルを削除
356
  if os.path.exists(local_path):
357
  os.remove(local_path)
358
  logger.info(f"Removed local old version file: {local_path}")
@@ -402,7 +398,7 @@ class CivitAICrawler:
402
  return f"{repo_id}1"
403
 
404
  # =============================================================================
405
- # 暗号化しないアップロード(ログや model_list.log 用)
406
  # =============================================================================
407
  def upload_file_raw(
408
  self,
@@ -448,26 +444,7 @@ class CivitAICrawler:
448
  raise
449
 
450
  # =============================================================================
451
- # 暗号化してアップロード (単ファイル)
452
- # =============================================================================
453
- def upload_file_encrypted(
454
- self,
455
- file_path: str,
456
- repo_id: Optional[str] = None,
457
- path_in_repo: Optional[str] = None
458
- ):
459
- if repo_id is None:
460
- repo_id = self.repo_ids['current']
461
- base_path = path_in_repo or ""
462
-
463
- self.encrypt_with_rclone(file_path)
464
- self.upload_encrypted_files(repo_id=repo_id, base_path_in_repo=base_path)
465
-
466
- if os.path.isdir(self.config.ENCRYPTED_DIR):
467
- shutil.rmtree(self.config.ENCRYPTED_DIR, ignore_errors=True)
468
-
469
- # =============================================================================
470
- # 暗号化してアップロード (フォルダ)
471
  # =============================================================================
472
  def upload_folder_encrypted(
473
  self,
@@ -475,12 +452,18 @@ class CivitAICrawler:
475
  repo_id: Optional[str] = None,
476
  path_in_repo: Optional[str] = None
477
  ) -> str:
 
 
 
 
478
  if repo_id is None:
479
  repo_id = self.repo_ids['current']
480
  base_path = path_in_repo or ""
481
 
 
482
  self.encrypt_with_rclone(folder_path)
483
 
 
484
  top_levels = [
485
  d for d in os.listdir(self.config.ENCRYPTED_DIR)
486
  if os.path.isdir(os.path.join(self.config.ENCRYPTED_DIR, d))
@@ -491,56 +474,35 @@ class CivitAICrawler:
491
  logger.warning(f"Multiple top-level folders found after encryption? {top_levels}. Using the first one.")
492
 
493
  encrypted_top_name = top_levels[0]
494
-
495
  self.upload_encrypted_files(repo_id=repo_id, base_path_in_repo=base_path)
496
 
 
497
  if os.path.isdir(self.config.ENCRYPTED_DIR):
498
  shutil.rmtree(self.config.ENCRYPTED_DIR, ignore_errors=True)
499
 
500
  return encrypted_top_name
501
 
502
  # =============================================================================
503
- # model_list.log の読み書きを「model_id: model_hf_url」で扱うよう変更
504
  # =============================================================================
505
- def read_model_list(self):
506
- """
507
- model_list.log の各行を
508
- "123456: https://huggingface.co/...encrypted_folder_name"
509
- の形式で読み込み、 { "123456": "https://huggingface.co/..."} の dict を返す
510
- """
511
- model_list = {}
512
- try:
513
- with open(self.config.LIST_FILE, "r", encoding="utf-8") as f:
514
- for line in f:
515
- line = line.strip()
516
- if not line:
517
- continue
518
- parts = line.split(": ", 1)
519
- if len(parts) == 2:
520
- stored_id, stored_url = parts
521
- model_list[stored_id] = stored_url
522
- return model_list
523
- except Exception as e:
524
- logger.error(f"Failed to read model list: {e}")
525
- return {}
526
-
527
  def process_model(self, model_url: str):
528
  """
529
- - 最新バージョン(+ images, html, info.json)をフォルダに一括DL 最後にまとめて暗号化アップロード
530
- - old_versions のファイルは 1つずつ「old_versions/」へDL → 即暗号化アップロード
531
  """
532
  try:
533
  model_id = model_url.rstrip("/").split("/")[-1]
534
  model_info = self.get_model_info(model_id)
 
 
 
535
 
536
  model_versions = model_info.get("modelVersions", [])
537
  if not model_versions:
538
  logger.warning(f"No versions found for model ID {model_id}")
539
  return
540
 
541
- # ==================================================================
542
- # 1) 「古いコード」と同じロジックでフォルダ名を決定
543
- # ==================================================================
544
  latest_version = model_versions[0]
545
  model_file = next(
546
  (file for file in latest_version["files"] if file.get('type') == 'Model'),
@@ -550,48 +512,37 @@ class CivitAICrawler:
550
  latest_filename = model_file['name']
551
  folder = os.path.splitext(latest_filename)[0]
552
  else:
 
553
  first_file = latest_version["files"][0]
554
  latest_filename = first_file['name']
555
  folder = os.path.splitext(latest_filename)[0]
556
  logger.warning(f"No 'Model' type file found for model ID {model_id}. Using first file's name.")
557
 
 
558
  os.makedirs(folder, exist_ok=True)
559
 
560
- # ==================================================================
561
- # 2) 最新バージョンをまとめてダウンロード (model本体)
562
- # + 画像ダウンロード + HTML保存 + info.json保存
563
- # ==================================================================
564
  self.download_model(model_versions, folder)
565
  self.download_images(model_versions, folder)
566
- self.save_html_content(model_url, folder)
567
  self.save_model_info(model_info, folder)
568
 
569
- # ==================================================================
570
- # 3) フォルダ全体(最新バージョン + images + html + info.json)を
571
- # まとめて 1 回だけ暗号化→アップロード
572
- # ==================================================================
573
  encrypted_top_name = self.upload_folder_encrypted(folder)
574
  logger.info(f"[MAIN] Uploaded latest version folder => {encrypted_top_name}")
575
 
576
- # ローカルフォルダ削除
577
  shutil.rmtree(folder, ignore_errors=True)
578
 
579
- # ==================================================================
580
- # 4) 古いバージョンのファイルを 1つずつDL → 即アップロード
581
- # ==================================================================
582
  if len(model_versions) > 1:
583
- # 旧バージョンのファイルだけ個別処理
584
  for version in model_versions[1:]:
585
  for file_info in version["files"]:
586
- # ***** 修正ポイント *****
587
- # download_old_version_file_and_upload(file_info, parent_folder, encrypted_top_name)
588
  self.download_old_version_file_and_upload(file_info, folder, encrypted_top_name)
589
- # ↑ parent_folder=folder に old_versionsフォルダを作り
590
- # そこへダウンロードしてから暗号化アップロード
591
 
592
- # ==================================================================
593
- # 5) model_list.log への登録やその他の処理
594
- # ==================================================================
595
  modelpage_name = model_info.get("name", f"Model_{model_id}")
596
  model_hf_url = f"https://huggingface.co/{self.repo_ids['current']}/tree/main/{encrypted_top_name}"
597
 
@@ -601,13 +552,17 @@ class CivitAICrawler:
601
  except Exception as e:
602
  logger.error(f"Unexpected error in process_model ({model_url}): {e}")
603
 
 
 
 
604
  async def crawl(self):
605
  """モデルを定期的にチェックし、更新を行う。"""
606
  while True:
607
  try:
 
608
  login(token=self.config.HUGGINGFACE_API_KEY, add_to_git_credential=True)
609
 
610
- # model_list.log & civitai_backup.log を取得
611
  model_list_path = hf_hub_download(repo_id=self.repo_ids['model_list'], filename=self.config.LIST_FILE)
612
  shutil.copyfile(model_list_path, f"./{self.config.LIST_FILE}")
613
 
@@ -670,7 +625,7 @@ class CivitAICrawler:
670
  f.write(f"{self.repo_ids['current']}\n")
671
  logger.info(f"Updated log file with new model ID: {model_id}")
672
 
673
- # ログとmodel_list.logをアップロード
674
  self.upload_file_raw(
675
  file_path=self.config.LOG_FILE,
676
  repo_id=self.repo_ids["log"],
 
104
 
105
  def encrypt_with_rclone(self, local_path: str):
106
  """
107
+ local_path がフォルダかファイルかで処理を分け、暗号化(rclone copy or copyto)を行う。
108
+ 暗号化先は self.config.ENCRYPTED_DIR に生成される。
 
109
  """
110
  if not os.path.exists(local_path):
111
  raise FileNotFoundError(f"[ERROR] Local path not found: {local_path}")
 
146
  f"[ERROR] {self.config.ENCRYPTED_DIR} not found. Check rclone config."
147
  )
148
 
 
149
  def upload_encrypted_files(self, repo_id: str, base_path_in_repo: str = ""):
150
+ """
151
+ 暗号化後の self.config.ENCRYPTED_DIR を走査し、
152
+ すべてのファイルを1つずつアップロードする。
153
+
154
+ (「最新バージョンのフォルダをまとめてアップロードするとき」は、1回だけこのメソッドを呼び出して
155
+ フォルダ分のファイルを一括アップロード。
156
+ old_versions でも 1ファイル暗号化後にこのメソッドを呼び出しているため、
157
+ そこだけは引き続き個別ファイルのアップロードとなる。)
158
+ """
159
  max_retries = 5
160
  for root, dirs, files in os.walk(self.config.ENCRYPTED_DIR):
161
  for fn in files:
 
181
  attempt += 1
182
  error_message = str(e)
183
 
184
+ # 429制限等のリトライロジック
 
 
 
 
185
  if "rate-limited" in error_message and "minutes" in error_message:
 
186
  match = re.search(r"in (\d+) minutes?", error_message)
187
  if match:
188
  minutes = int(match.group(1))
 
189
  minutes += 1
190
  logger.warning(f"Rate-limited. Waiting {minutes} minutes before retry...")
191
  time.sleep(minutes * 60)
192
+ attempt -= 1
193
  continue
194
 
 
 
 
195
  if "you can retry this action in about 1 hour" in error_message:
196
  logger.warning("Encountered 'retry in 1 hour' error. Waiting 1 hour before retrying...")
197
  time.sleep(3600)
198
+ attempt -= 1
199
  continue
200
 
201
  if "over the limit of 100000 files" in error_message:
 
253
  def download_model(self, model_versions: list, folder: str):
254
  """
255
  - 最新バージョンのファイルを「folder」へまとめてダウンロード
256
+ - ここでは古いバージョンはダウンロードしない(後ほど old_versions にて個別にダウンロード)
 
257
  """
258
  latest_version = model_versions[0]
259
  latest_files = latest_version["files"]
 
292
  def download_old_version_file_and_upload(self, file_info, parent_folder: str, encrypted_top_name: str):
293
  """
294
  古いバージョンのファイルを1つダウンロード→暗号化アップロード→削除。
295
+ old_versions フォルダの中に都度ファイルを落としてアップロード→削除する。
296
  """
297
  file_name = file_info["name"]
298
  download_url = file_info["downloadUrl"]
299
 
300
+ # old_versions フォルダ作成
301
  old_versions_folder = os.path.join(parent_folder, "old_versions")
302
  os.makedirs(old_versions_folder, exist_ok=True)
303
 
 
306
  login_detected_count = 0
307
  while login_detected_count < 5:
308
  try:
 
309
  self.download_file(download_url, old_versions_folder, file_name)
310
  except Exception as e:
311
  logger.error(f"Exception while downloading old file {file_name}: {e}")
 
322
  break
323
 
324
  if login_detected_count >= 5:
325
+ # 5回失敗した場合はダミーファイル化
326
  dummy_file_name = f"{file_name}.download_failed"
327
  dummy_file_path = os.path.join(old_versions_folder, dummy_file_name)
328
  try:
 
333
  logger.error(f"Failed to create dummy file for old version {file_name}: {e}")
334
  return
335
 
336
+ # ここから暗号化→アップロード→削除 1ファイルずつ処理を繰り返す)
337
  try:
338
+ # 1) 単ファイルを暗号化
339
  self.encrypt_with_rclone(local_path)
340
 
341
+ # 2) アップロード
342
  self.upload_encrypted_files(
343
  repo_id=self.repo_ids["current"],
344
  base_path_in_repo=f"{encrypted_top_name}/old_versions"
 
348
  except Exception as e:
349
  logger.error(f"Error uploading old version file {file_name}: {e}")
350
 
351
+ # 3) ローカルの平文ファイルを削除
352
  if os.path.exists(local_path):
353
  os.remove(local_path)
354
  logger.info(f"Removed local old version file: {local_path}")
 
398
  return f"{repo_id}1"
399
 
400
  # =============================================================================
401
+ # ログ類のアップロード(生ファイル)
402
  # =============================================================================
403
  def upload_file_raw(
404
  self,
 
444
  raise
445
 
446
  # =============================================================================
447
+ # フォルダ全体をまとめて暗号化してアップロード
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  # =============================================================================
449
  def upload_folder_encrypted(
450
  self,
 
452
  repo_id: Optional[str] = None,
453
  path_in_repo: Optional[str] = None
454
  ) -> str:
455
+ """
456
+ 指定フォルダを暗号化し、暗号化後のファイルを一括アップロードする。
457
+ 戻り値: 暗号化されたトップレベルフォルダ名
458
+ """
459
  if repo_id is None:
460
  repo_id = self.repo_ids['current']
461
  base_path = path_in_repo or ""
462
 
463
+ # 1) 全体を一度に暗号化
464
  self.encrypt_with_rclone(folder_path)
465
 
466
+ # 2) 暗号化後フォルダ名を取得し、一括アップロード
467
  top_levels = [
468
  d for d in os.listdir(self.config.ENCRYPTED_DIR)
469
  if os.path.isdir(os.path.join(self.config.ENCRYPTED_DIR, d))
 
474
  logger.warning(f"Multiple top-level folders found after encryption? {top_levels}. Using the first one.")
475
 
476
  encrypted_top_name = top_levels[0]
 
477
  self.upload_encrypted_files(repo_id=repo_id, base_path_in_repo=base_path)
478
 
479
+ # 3) 暗号化用ディレクトリをクリーンアップ
480
  if os.path.isdir(self.config.ENCRYPTED_DIR):
481
  shutil.rmtree(self.config.ENCRYPTED_DIR, ignore_errors=True)
482
 
483
  return encrypted_top_name
484
 
485
  # =============================================================================
486
+ # メイン処理:最新バージョンは「フォルダ一括」アップロード、old_versions は1ファイルずつ
487
  # =============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  def process_model(self, model_url: str):
489
  """
490
+ 1) 最新バージョンフォルダにまとめてダウンロードし、1回だけ暗号化&アップロード
491
+ 2) old_versions 1ファイルずつダウンロード→暗号化アップロード→削除
492
  """
493
  try:
494
  model_id = model_url.rstrip("/").split("/")[-1]
495
  model_info = self.get_model_info(model_id)
496
+ if not model_info:
497
+ logger.warning(f"Cannot get model info: {model_id}")
498
+ return
499
 
500
  model_versions = model_info.get("modelVersions", [])
501
  if not model_versions:
502
  logger.warning(f"No versions found for model ID {model_id}")
503
  return
504
 
505
+ # === フォルダ名決定(最新バージョンのファイル名から取得) ===
 
 
506
  latest_version = model_versions[0]
507
  model_file = next(
508
  (file for file in latest_version["files"] if file.get('type') == 'Model'),
 
512
  latest_filename = model_file['name']
513
  folder = os.path.splitext(latest_filename)[0]
514
  else:
515
+ # type='Model' がなければ最初のファイルを基準に
516
  first_file = latest_version["files"][0]
517
  latest_filename = first_file['name']
518
  folder = os.path.splitext(latest_filename)[0]
519
  logger.warning(f"No 'Model' type file found for model ID {model_id}. Using first file's name.")
520
 
521
+ # ダウンロード先フォルダ作成
522
  os.makedirs(folder, exist_ok=True)
523
 
524
+ # === 最新バージョンをまとめてダウンロード ===
 
 
 
525
  self.download_model(model_versions, folder)
526
  self.download_images(model_versions, folder)
527
+ self.save_html_content(self.config.URLS["modelPage"] + str(model_id), folder)
528
  self.save_model_info(model_info, folder)
529
 
530
+ # === フォルダごと暗号化→アップロード ===
 
 
 
531
  encrypted_top_name = self.upload_folder_encrypted(folder)
532
  logger.info(f"[MAIN] Uploaded latest version folder => {encrypted_top_name}")
533
 
534
+ # ローカルのフォルダ削除
535
  shutil.rmtree(folder, ignore_errors=True)
536
 
537
+ # === 古いバージョンは1つずつアップロード ===
 
 
538
  if len(model_versions) > 1:
539
+ # 旧バージョンだけ個別ファイル処理
540
  for version in model_versions[1:]:
541
  for file_info in version["files"]:
542
+ # old_versions フォルダに落としてアップロード
 
543
  self.download_old_version_file_and_upload(file_info, folder, encrypted_top_name)
 
 
544
 
545
+ # === model_list.log への追加など後処理 ===
 
 
546
  modelpage_name = model_info.get("name", f"Model_{model_id}")
547
  model_hf_url = f"https://huggingface.co/{self.repo_ids['current']}/tree/main/{encrypted_top_name}"
548
 
 
552
  except Exception as e:
553
  logger.error(f"Unexpected error in process_model ({model_url}): {e}")
554
 
555
+ # =============================================================================
556
+ # 定期巡回して新しいモデルがあれば処理する
557
+ # =============================================================================
558
  async def crawl(self):
559
  """モデルを定期的にチェックし、更新を行う。"""
560
  while True:
561
  try:
562
+ # HuggingFaceログイン
563
  login(token=self.config.HUGGINGFACE_API_KEY, add_to_git_credential=True)
564
 
565
+ # 既存の model_list.log & civitai_backup.log を取得してローカルへ
566
  model_list_path = hf_hub_download(repo_id=self.repo_ids['model_list'], filename=self.config.LIST_FILE)
567
  shutil.copyfile(model_list_path, f"./{self.config.LIST_FILE}")
568
 
 
625
  f.write(f"{self.repo_ids['current']}\n")
626
  logger.info(f"Updated log file with new model ID: {model_id}")
627
 
628
+ # ログとmodel_list.logをアップロード (暗号化しない)
629
  self.upload_file_raw(
630
  file_path=self.config.LOG_FILE,
631
  repo_id=self.repo_ids["log"],