jm图片的分割算法以及获取封面

本知识库目录
Python3 行展开代码收起代码
import io, hashlib, requests
from PIL import Image
from math import floor

用到的模块

Python25 行展开代码收起代码
def get_num(album_id:str, index:str):
    normalCutNum = 10  # 默认切割数
    aIndex = album_id + index
    album_id = int(album_id)
    aIndex = ord(hashlib.md5(aIndex.encode()).hexdigest()[-1])  # md5编码后 取最后一位 获取所在unicode码表位置
    if album_id >= 268850 and album_id <= 421925:
        aIndex %= 10
    elif album_id >= 421926:
        aIndex %= 8

    switcher = {
        0: 2,
        1: 4,
        2: 6,
        3: 8,
        4: 10,
        5: 12,
        6: 14,
        7: 16,
        8: 18,
        9: 20
    }

    normalCutNum = switcher.get(aIndex, normalCutNum)
    return normalCutNum

输入本子id以及图片编号(如:00001),返回的值为分割数。

Python32 行展开代码收起代码
def hb(img_data, cut_num):
    # 打开图片
    bin_stream = io.BytesIO(img_data)
    img = Image.open(bin_stream)
    img_width, img_height = img.size

    # 计算切割数量和偏移量
    offset = img_height % cut_num  # 最后一张图片较高需要偏移高度

    # 创建新的空白图像用于拼接
    decoded_img = Image.new('RGB', (img_width, img_height))

    # 分割并重新拼接
    for i in range(cut_num):
        cut_height = floor(img_height / cut_num)
        y_coordinate = cut_height * i
        end_coordinate = img_height - cut_height * (i + 1) - offset

        if i == 0:
            cut_height += offset
        else:
            y_coordinate += offset

        # 裁剪和粘贴
        crop_img = img.crop((0, end_coordinate, img_width, end_coordinate + cut_height))
        decoded_img.paste(crop_img, (0, y_coordinate))

    # 将处理后的图片保存为字节数据
    with io.BytesIO() as buffer:
        decoded_img.save(buffer, format="PNG")
        data = buffer.getvalue()
    return data

输入本子的图片和图片的字节数据,返回拼接好后的字节数据。

封装到一起的话为:

Python83 行展开代码收起代码
import io
import hashlib
from PIL import Image
from math import floor

def encode_JM_single_image(album_id, img_data: bytes, index: (str | int)):
    """
    对单张图片进行处理,将图片按照指定的切割数分割并重新拼接。
    
    :param album_id: 专辑 ID,用于算法计算。
    :param img_data: 图像数据,字节形式。
    :param index: 图像索引,作为分割计算的一部分。
    :return: 处理后的图片数据,字节形式。
    """
    def get_num(album_id: str, index: str):
        """
        计算切割数量的逻辑,基于 album_id 和 index。
        """
        normalCutNum = 10  # 默认切割数
        aIndex = album_id + index
        album_id = int(album_id)
        aIndex = ord(hashlib.md5(aIndex.encode()).hexdigest()[-1])  # md5编码后取最后一位

        if album_id >= 268850 and album_id <= 421925:
            aIndex %= 10
        elif album_id >= 421926:
            aIndex %= 8

        switcher = {
            0: 2,
            1: 4,
            2: 6,
            3: 8,
            4: 10,
            5: 12,
            6: 14,
            7: 16,
            8: 18,
            9: 20
        }

        normalCutNum = switcher.get(aIndex, normalCutNum)
        return normalCutNum

    # 确保 index 是 5 位数的字符串
    if isinstance(index, int):
        index = str(index).zfill(5)
    else:
        index = index.zfill(5)

    # 打开图片
    bin_stream = io.BytesIO(img_data)
    img = Image.open(bin_stream)
    img_width, img_height = img.size

    # 计算切割数量和偏移量
    cut_num = get_num(album_id, index)
    offset = img_height % cut_num  # 偏移高度

    # 创建新的空白图像用于拼接
    decoded_img = Image.new('RGB', (img_width, img_height))

    # 分割并重新拼接
    for i in range(cut_num):
        cut_height = floor(img_height / cut_num)
        y_coordinate = cut_height * i
        end_coordinate = img_height - cut_height * (i + 1) - offset

        if i == 0:
            cut_height += offset
        else:
            y_coordinate += offset

        # 裁剪和粘贴
        crop_img = img.crop((0, end_coordinate, img_width, end_coordinate + cut_height))
        decoded_img.paste(crop_img, (0, y_coordinate))

    # 将处理后的图片保存为字节数据
    with io.BytesIO() as buffer:
        decoded_img.save(buffer, format="PNG")
        data = buffer.getvalue()
    
    return data
HTML, XML7 行展开代码收起代码
未加密的本子123456
<div class="center scramble-page spnotice_chk" id="00001.webp">  
<img src="https://cdn-msp.18comic.vip/media/photos/123456/00001.webp" data-original="https://cdn-msp.18comic.vip/media/photos/123456/00001.webp" id="album_photo_00001.webp" class="lazy_img img-responsive-mw lazy-loaded" data-page="0"></div>
加密过的本子954211
<div class="center scramble-page spnotice_chk" id="00001.webp">  
<img src="https://cdn-msp2.18comic.vip/media/photos/954211/00001.webp" data-original="https://cdn-msp2.18comic.vip/media/photos/954211/00001.webp" id="album_photo_00001.webp" class="lazy_img img-responsive-mw lazy-loaded hide" data-page="0">
<canvas width="960" height="796" style="width: 960px; display: block;" data="eyJ1cmwiOiJodHRwczovL2Nkbi1tc3AyLjE4Y29taWMudmlwL21lZGlhL3Bob3Rvcy85NTQyMTEvMDAwMDEud2VicCIsImFyZ3MiOltbMCw3MjYsOTYwLDcwLDAsMCw5NjAsNzBdLFswLDY2MCw5NjAsNjYsMCw3MCw5NjAsNjZdLFswLDU5NCw5NjAsNjYsMCwxMzYsOTYwLDY2XSxbMCw1MjgsOTYwLDY2LDAsMjAyLDk2MCw2Nl0sWzAsNDYyLDk2MCw2NiwwLDI2OCw5NjAsNjZdLFswLDM5Niw5NjAsNjYsMCwzMzQsOTYwLDY2XSxbMCwzMzAsOTYwLDY2LDAsNDAwLDk2MCw2Nl0sWzAsMjY0LDk2MCw2NiwwLDQ2Niw5NjAsNjZdLFswLDE5OCw5NjAsNjYsMCw1MzIsOTYwLDY2XSxbMCwxMzIsOTYwLDY2LDAsNTk4LDk2MCw2Nl0sWzAsNjYsOTYwLDY2LDAsNjY0LDk2MCw2Nl0sWzAsMCw5NjAsNjYsMCw3MzAsOTYwLDY2XV19"></canvas></div>

可以看到jm的加密是在网页中实现的,不过由于我不懂html和css,js所以没找到jm的加密css。 data是由base64加密过的图片链接和分割坐标,通过模拟浏览器应该可以获得,我没有去尝试。 可以看到是否加密的区别在于class中是否有hide,有了这一点再结合上面的分割算法即可完成判断以及还原,不过class标签只有在photo中才有。

0%