Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/develop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
password: ${{ secrets.GITHUB_TOKEN }}

# =======================
# booth-checker
Expand Down Expand Up @@ -92,4 +92,4 @@ jobs:
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_BOOTH_DISCORD }}
subject-digest: ${{ steps.push_booth_discord.outputs.digest }}
push-to-registry: true
push-to-registry: true
4 changes: 2 additions & 2 deletions .github/workflows/latest-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
password: ${{ secrets.GITHUB_TOKEN }}

# =======================
# booth-checker
Expand Down Expand Up @@ -104,4 +104,4 @@ jobs:
- name: Publish release
uses: ncipollo/release-action@v1
with:
generateReleaseNotes: true
generateReleaseNotes: true
54 changes: 8 additions & 46 deletions booth_checker/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
from operator import length_hint
from unitypackage_extractor.extractor import extractPackage

try:
from .fbx_diff import build_fbx_path_list, calculate_fbx_diff
except ImportError:
from fbx_diff import build_fbx_path_list, calculate_fbx_diff

from shared import *
import booth
import booth_sql
Expand Down Expand Up @@ -254,7 +259,6 @@ def generate_changelog_and_summary(item_data, download_url_list, version_json):

return changelog_html_path, s3_object_url, summary_result, diff_found, None


def generate_fbx_changelog_and_summary(item_data, download_url_list, version_json):
"""Generates changelog information for FBX-only tracking."""
previous_fbx = version_json.get('fbx-files', {}) or {}
Expand All @@ -269,46 +273,13 @@ def generate_fbx_changelog_and_summary(item_data, download_url_list, version_jso
logger.error(f'An error occurred while parsing {filename}: {e}')
logger.debug(traceback.format_exc())

previous_hashes = {file_hash for file_hash in previous_fbx.values()}
current_hashes = {file_hash for file_hash in current_fbx.values()}

added = []
changed = []
deleted = []

previous_remaining = dict(previous_fbx)
current_remaining = dict(current_fbx)

for name in set(previous_fbx.keys()) & set(current_fbx.keys()):
old_hash = previous_fbx[name]
new_hash = current_fbx[name]
if old_hash != new_hash:
changed.append(name)
previous_remaining.pop(name, None)
current_remaining.pop(name, None)

for name, new_hash in current_remaining.items():
if new_hash in previous_hashes:
continue
added.append(name)

for name, old_hash in previous_remaining.items():
if old_hash in current_hashes:
continue
deleted.append(name)
added_entries, changed_entries, deleted_entries = calculate_fbx_diff(previous_fbx, current_fbx)

if not added and not changed and not deleted:
if not added_entries and not changed_entries and not deleted_entries:
logger.info('No FBX hash differences detected; skipping changelog generation.')
return None, None, None, False, current_fbx

path_list = []
for name in sorted(added):
path_list.append({'line_str': name, 'status': 1})
for name in sorted(changed):
path_list.append({'line_str': name, 'status': 3})
for name in sorted(deleted):
path_list.append({'line_str': name, 'status': 2})

path_list = build_fbx_path_list(added_entries, changed_entries, deleted_entries)
tree = build_tree(path_list)
html_list_items = tree_to_html(tree) if item_data["changelog_show"] else ''
summary_data = files_list(tree)
Expand Down Expand Up @@ -874,15 +845,6 @@ def strftime_now():
while True:
logger.info("BoothChecker cycle started")

# BOOTH Heartbeat check once per cycle
try:
logger.info('Checking BOOTH heartbeat')
requests.get("https://booth.pm", timeout=10)
except requests.RequestException as e:
logger.error(f'BOOTH heartbeat failed: {e}. Skipping this cycle.')
sleep(refresh_interval)
continue

# Recreate temporary folders
recreate_folder("./download")
recreate_folder("./process")
Expand Down
35 changes: 24 additions & 11 deletions booth_checker/booth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ def _extract_download_info(div, link_selector, filename_selector):
if not download_link or not filename_div:
return None

href = download_link.get("data-href")
filename = filename_div.get_text()
href = download_link.get("data-href") or download_link.get("href")
filename = filename_div.get_text(strip=True)

if not href:
if not href or not filename:
return None

href = re.sub(r'[^0-9]', '', href)
return [href, filename]
match = re.search(r'/downloadables/(\d+)', href)
if not match:
return None

return [match.group(1), filename]

def _crawling_base(url, cookie, selectors, shortlist, thumblist, product_only_filter=None):
response = requests.get(url=url, cookies=cookie)
Expand Down Expand Up @@ -72,9 +75,19 @@ def crawling(order_num, product_only, cookie, shortlist=None, thumblist=None):
'product_info_selector': 'a',
'product_info_index': 1,
'thumb_selector': 'img',
'download_item_selector': 'div.legacy-list-item__center, div[data-test="downloadable"]',
'download_link_selector': 'a.nav-reverse, div.js-download-button',
'filename_selector': 'div.flex-\\[1\\] b'
'download_item_selector': (
'div.legacy-list-item__center, '
'div.mt-16.desktop\\:flex.desktop\\:justify-between.desktop\\:items-center'
),
'download_link_selector': (
'div.js-download-button[data-test="downloadable"][data-href*="/downloadables/"], '
'a.nav-reverse[href*="/downloadables/"]'
),
'filename_selector': (
'div.min-w-0.u-text-wrap b, '
'div.min-w-0.break-words.whitespace-pre-line, '
'div.flex-\\[1\\] b'
)
}
return _crawling_base(url, cookie, selectors, shortlist, thumblist, product_only_filter=product_only)

Expand All @@ -85,9 +98,9 @@ def crawling_gift(order_num, cookie, shortlist=None, thumblist=None):
'product_div_class': 'rounded-16 bg-white p-40 mobile:px-16 mobile:pt-24 mobile:pb-40 mobile:rounded-none',
'product_info_selector': 'div.mt-24.text-left a',
'thumb_selector': 'img',
'download_item_selector': 'div.w-full.text-left, div[data-test="downloadable"]',
'download_link_selector': 'a.no-underline.flex.items-center.flex.gap-4, div.js-download-button',
'filename_selector': "div[class='min-w-0 break-words whitespace-pre-line']"
'download_item_selector': 'div.mt-16.desktop\\:flex.desktop\\:justify-between.desktop\\:items-center',
'download_link_selector': 'div.js-download-button[data-test="downloadable"][data-href*="/downloadables/"]',
'filename_selector': 'div.min-w-0.break-words.whitespace-pre-line, div.min-w-0.u-text-wrap b'
}
return _crawling_base(url, cookie, selectors, shortlist, thumblist)

Expand Down
167 changes: 167 additions & 0 deletions booth_checker/fbx_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import os
from collections import Counter, defaultdict


def _normalize_fbx_entries(fbx_records):
entries = []
for path_str, file_hash in sorted(fbx_records.items()):
entries.append(
{
"path": path_str,
"basename": os.path.basename(path_str),
"hash": file_hash,
}
)
return entries


def _remove_exact_name_hash_matches(previous_entries, current_entries):
previous_by_key = defaultdict(list)
current_by_key = defaultdict(list)

for entry in previous_entries:
previous_by_key[(entry["basename"], entry["hash"])].append(entry)
for entry in current_entries:
current_by_key[(entry["basename"], entry["hash"])].append(entry)

remaining_previous = []
remaining_current = []

for key in sorted(set(previous_by_key) | set(current_by_key)):
previous_list = previous_by_key.get(key, [])
current_list = current_by_key.get(key, [])
shared_count = min(len(previous_list), len(current_list))

remaining_previous.extend(previous_list[shared_count:])
remaining_current.extend(current_list[shared_count:])

return remaining_previous, remaining_current


def calculate_fbx_diff(previous_fbx, current_fbx):
previous_entries = _normalize_fbx_entries(previous_fbx)
current_entries = _normalize_fbx_entries(current_fbx)

remaining_previous, remaining_current = _remove_exact_name_hash_matches(
previous_entries, current_entries
)

previous_by_name = defaultdict(list)
current_by_name = defaultdict(list)

for entry in remaining_previous:
previous_by_name[entry["basename"]].append(entry)
for entry in remaining_current:
current_by_name[entry["basename"]].append(entry)

added = []
changed = []
deleted = []

for name in sorted(set(previous_by_name) | set(current_by_name)):
previous_list = sorted(
previous_by_name.get(name, []),
key=lambda entry: (entry["hash"], entry["path"]),
)
current_list = sorted(
current_by_name.get(name, []),
key=lambda entry: (entry["hash"], entry["path"]),
)

shared_count = min(len(previous_list), len(current_list))
for index in range(shared_count):
changed.append(
{
"basename": name,
"previous_hash": previous_list[index]["hash"],
"current_hash": current_list[index]["hash"],
"previous_path": previous_list[index]["path"],
"current_path": current_list[index]["path"],
}
)

deleted.extend(previous_list[shared_count:])
added.extend(current_list[shared_count:])

return added, changed, deleted


def _short_hash(file_hash):
return file_hash[:8]


def _append_path_context(label, basename, basename_counts, previous_path=None, current_path=None):
is_ambiguous_name = basename_counts[basename] > 1
path_changed = previous_path and current_path and previous_path != current_path

if not is_ambiguous_name and not path_changed:
return label

if previous_path and current_path:
if previous_path == current_path:
return f"{label} {{{current_path}}}"
return f"{label} {{from {previous_path} -> {current_path}}}"

path_value = current_path or previous_path
return f"{label} {{{path_value}}}"


def build_fbx_path_list(added_entries, changed_entries, deleted_entries):
basename_counts = Counter()

for entry in added_entries:
basename_counts[entry["basename"]] += 1
for entry in changed_entries:
basename_counts[entry["basename"]] += 1
for entry in deleted_entries:
basename_counts[entry["basename"]] += 1

path_list = []

for entry in added_entries:
label = _append_path_context(
entry["basename"],
entry["basename"],
basename_counts,
current_path=entry["path"],
)
path_list.append(
{
"line_str": f"{label} [new {_short_hash(entry['hash'])}]",
"status": 1,
}
)

for entry in changed_entries:
label = _append_path_context(
entry["basename"],
entry["basename"],
basename_counts,
previous_path=entry["previous_path"],
current_path=entry["current_path"],
)
path_list.append(
{
"line_str": (
f"{label} "
f"[{_short_hash(entry['previous_hash'])} -> {_short_hash(entry['current_hash'])}]"
),
"status": 3,
}
)

for entry in deleted_entries:
label = _append_path_context(
entry["basename"],
entry["basename"],
basename_counts,
previous_path=entry["path"],
)
path_list.append(
{
"line_str": f"{label} [old {_short_hash(entry['hash'])}]",
"status": 2,
}
)

return path_list
Loading
Loading