Open faafbb opened 2 years ago
我回复一下,猜中了一半,这是一个HmacSHA256
POST https://www.dmm.co.jp/service/digitalapi/-/json/=/method=PcApp/ HTTP/1.1
Host: www.dmm.co.jp
User-Agent: UnityPlayer/2019.2.17f1 (UnityWebRequest/1.0, libcurl/7.52.0-DEV)
Accept: */*
Accept-Encoding: identity
Authorization: Bearer {token}
Content-Type: application/octet-stream
X-Unity-Version: 2019.2.17f1
Content-Length: 492
message=Digital_Api_PlayableProvider.getContentInfo¶ms={"transfer_type":"stream","quality":"auto","device":"pc_vr","vr_appli_type":"pc","adult_flag":false,"product_id":"sivr00135","exploit_id":"uid:{uid}","part":"1","shop":"videoa","HTTP_USER_AGENT":"WINDOWSVR_DMMVRPLAY 2.0.5 Intel(R) HD Graphics 4000","is_past":true,"parent_product_id":"sivr00135dl6","quality_groups":["high"]}&appid=pc_movievrplayer&authkey={authkey}
感谢兄弟分享方法 我试了你的工程但不好搞了 token也正确…为什么呢
我回复一下,猜中了一半,这是一个HmacSHA256
HmacSHA256加密是可以配置密钥的,应该只能通过反编译播放器来获取这个密钥了(触及知识盲区了)
POST https://www.dmm.co.jp/service/digitalapi/-/json/=/method=PcApp/ HTTP/1.1 Host: www.dmm.co.jp User-Agent: UnityPlayer/2019.2.17f1 (UnityWebRequest/1.0, libcurl/7.52.0-DEV) Accept: */* Accept-Encoding: identity Authorization: Bearer {token} Content-Type: application/octet-stream X-Unity-Version: 2019.2.17f1 Content-Length: 492 message=Digital_Api_PlayableProvider.getContentInfo¶ms={"transfer_type":"stream","quality":"auto","device":"pc_vr","vr_appli_type":"pc","adult_flag":false,"product_id":"sivr00135","exploit_id":"uid:{uid}","part":"1","shop":"videoa","HTTP_USER_AGENT":"WINDOWSVR_DMMVRPLAY 2.0.5 Intel(R) HD Graphics 4000","is_past":true,"parent_product_id":"sivr00135dl6","quality_groups":["high"]}&appid=pc_movievrplayer&authkey={authkey}
感谢兄弟分享方法 我试了你的工程但不好搞了 token也正确…为什么呢
POST https://www.dmm.co.jp/service/digitalapi/-/json/=/method=PcApp/ HTTP/1.1
Host: www.dmm.co.jp
User-Agent: UnityPlayer/2019.2.17f1 (UnityWebRequest/1.0, libcurl/7.52.0-DEV)
Accept: */*
Accept-Encoding: identity
Authorization: Bearer {token}
Content-Type: application/octet-stream
X-Unity-Version: 2019.2.17f1
Content-Length: 492
message=Digital_Api_PlayableProvider.getContentInfo¶ms={"transfer_type":"stream","quality":"auto","device":"pc_vr","vr_appli_type":"pc","adult_flag":false,"product_id":"sivr00135","exploit_id":"uid:{uid}","part":"1","shop":"videoa","HTTP_USER_AGENT":"WINDOWSVR_DMMVRPLAY 2.0.5 Intel(R) HD Graphics 4000","is_past":true,"parent_product_id":"sivr00135dl6","quality_groups":["high"]}&appid=pc_movievrplayer&authkey={authkey}
fidder里的是http请求报文,从 Host 到 Content-Length 是请求头,最后一行才是请求体,其中请求头应该放在 headers
里,请求体放在body
里
谢谢回复 成功我得到返回值,并开始下载,但它总是在中间卡了。我试了很多次,都是就卡了,似乎在一半之后就卡不前,例如50%、70%等等。 你都下载了吗?有什么需要的选项吗?
环境:windows10,N_m3u8DL-CLI_v3.0.2
谢谢回复 成功我得到返回值,并开始下载,但它总是在中间卡了。我试了很多次,都是就卡了,似乎在一半之后就卡不前,例如50%、70%等等。 你都下载了吗?有什么需要的选项吗?
环境:windows10,N_m3u8DL-CLI_v3.0.2
视频开始播放之前把播放器关了就行了,如果已经开始播放了你再去发请求,那就会出现这种情况。
谢谢回复 成功我得到返回值,并开始下载,但它总是在中间卡了。我试了很多次,都是就卡了,似乎在一半之后就卡不前,例如50%、70%等等。 你都下载了吗?有什么需要的选项吗? 环境:windows10,N_m3u8DL-CLI_v3.0.2
视频开始播放之前把播放器关了就行了,如果已经开始播放了你再去发请求,那就会出现这种情况。
谢谢回复 嗯,我使用N_m3u8DL-CLI视频开始下载之前 先把DMM播放器立即就关了(视频开始播放之前) 然后开始下载视频 但是还是卡了 好奇怪 dmmvr播放器v2.0.5 难道这是个原因吗?
谢谢回复 成功我得到返回值,并开始下载,但它总是在中间卡了。我试了很多次,都是就卡了,似乎在一半之后就卡不前,例如50%、70%等等。 你都下载了吗?有什么需要的选项吗?
环境:windows10,N_m3u8DL-CLI_v3.0.2
我也是相同版本的播放器。抓包信息里面出现第二个 /service 的时候就可以把播放器关了。
谢谢回复 成功我得到返回值,并开始下载,但它总是在中间卡了。我试了很多次,都是就卡了,似乎在一半之后就卡不前,例如50%、70%等等。 你都下载了吗?有什么需要的选项吗? 环境:windows10,N_m3u8DL-CLI_v3.0.2
我也是相同版本的播放器。抓包信息里面出现第二个 /service 的时候就可以把播放器关了。
那没有关系vr播放器版本 嗯嗯我也一样的你的工程 可是每次卡了 我好次考虑原因是什么…想不到… 卡了原因是变了m3u8的地址 所以就卡了 看抓包信息是有的404error 可是正在下载的时候我什么都不做…
请问这个方法还有效吗,我用fiddler获取了请求,但是将请求按照上述例子在postman里填写后发送,返回值为403,接着我发现fiddler在获取请求后也接收了回复,于是我将redirect的值按例子的方法拼接上licenseUID的值填写到了M3U8地址栏,以及post里authkey的值填到了自定义KEY里,运行后大概下载了5M左右内容就报错闪退了
请问这个方法还有效吗,我用fiddler获取了请求,但是将请求按照上述例子在postman里填写后发送,返回值为403,接着我发现fiddler在获取请求后也接收了回复,于是我将redirect的值按例子的方法拼接上licenseUID的值填写到了M3U8地址栏,以及post里authkey的值填到了自定义KEY里,运行后大概下载了5M左右内容就报错闪退了
刚才继续分析了一下fiddler抓取的请求,发现自己犯了一个小白错误,我把笔者例子里的花括号也放进地址里了,然后我发现了更简洁的做法。
首先还是准备好系统代理,最新版的dmmVR,fiddler,DL。
浏览器打开流媒体跳转,依次会有以下请求,发送包选择查看RAW,接收包选择查看JSON,图1是调起播放器,图2是请求认证,图3就是我们需要的,直接把GET的值填到M3U8栏了就可以下载了。
新的发现,之前postman使用失败的原因是默认请求头为GET,实际应该改为POST,成功收到回复。两种方法收到的链接唯一区别在于443端口的显示,但是它是HTTPS的默认端口,所以就是一样的。 当我再次尝试下载时出现了大量的504错误,通过查看fiddler发现问题在于Connection: CLOSE,但是这个请求是基于HTTP/1.1的,默认应该会保持活跃。经过多次尝试之后发现原因在于我们只填写了url,请求头的参数是不完整的,正好下载器还提供了请求头的填写框,填上再发送,果然正常下载了。
暂行结论 使用service之后的首个digital链接 将url填入M3U8地址,即{https://至access=1} header填入请求头,即{GET至HTTP1.1}
你好,我根据你的方法进行下载 但是和你一样下载不了 但是如果挂了梯子,就会得到这个结果,一直卡在0bytes/s 到底哪里出问题了呢,纯小白第一次用fidder
你好,我根据你的方法进行下载 但是和你一样下载不了 但是如果挂了梯子,就会得到这个结果,一直卡在0bytes/s 到底哪里出问题了呢,纯小白第一次用fidder
import os
import tkinter as tk
CONFIG_PATH = "save_config.txt"
formatted_request = "" # Declare it as a global variable
def format_request(request, save_name, workdir):
request_line, headers = request.split('\n', 1)
method, url, _ = request_line.split(' ', 2)
headers = headers.split('\n')
headers = '|'.join(h for h in headers if h)
result = f"\"{url}\" --saveName \"{save_name}\""
# If the headers checkbox is checked, add headers to the result
if use_headers.get():
result += f" --headers \"{method} {url} HTTP/1.1|{headers}\""
if workdir:
result += f" --workDir \"{workdir}\""
return result
def generate_request():
"""Generates and returns the formatted request."""
global formatted_request # Use the global variable
raw_request = request_entry.get('1.0', tk.END)
save_name = save_name_entry.get()
workdir = workdir_entry.get() if workdir_entry.get() != '当前目录' else ''
formatted_request = format_request(raw_request, save_name, workdir)
if enable_del_after_done.get():
formatted_request += " --enableDelAfterDone"
if disable_date_info.get():
formatted_request += " --disableDateInfo"
return formatted_request
def submit():
request = generate_request()
os.system(f"start cmd /k N_m3u8DL-CLI_v3.0.2.exe {request}") # Use os.system instead
with open(CONFIG_PATH, 'w') as f:
f.write(f"{save_name_entry.get()}\n{workdir_entry.get()}\n")
def run_program(path):
os.startfile(path)
def paste(event):
content = root.clipboard_get()
event.widget.insert('insert', content)
def clear():
request_entry.delete('1.0', tk.END)
def clear_entry(event):
event.widget.delete(0, tk.END)
def workdir_focus_in(event):
workdir_label.pack_forget()
def workdir_focus_out(event):
if not workdir_entry.get():
workdir_label.pack(fill='both', expand=True)
def load_config():
if os.path.exists(CONFIG_PATH):
with open(CONFIG_PATH, 'r') as f:
lines = f.readlines()
if len(lines) > 0:
save_name_entry.insert(0, lines[0].strip())
if len(lines) > 1:
workdir_entry.insert(0, lines[1].strip())
if lines[1].strip():
workdir_label.pack_forget()
root = tk.Tk()
root.title("Request Formatter")
root.resizable(False, False)
root.attributes('-topmost', True)
window_width = 380
window_height = 420
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
position_top = int(screen_height / 2 - window_height / 2)
position_right = int(screen_width / 2 - window_width / 2)
root.geometry(f"{window_width}x{window_height}+{position_right}+{position_top}")
request_label = tk.Label(root, text="HTTP GET request:")
request_label.grid(row=0, column=0, columnspan=2)
request_entry = tk.Text(root, height=19, width=50)
request_entry.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 5))
request_entry.bind("<Button-3>", paste)
button_frame = tk.Frame(root)
button_frame.grid(row=2, column=0, columnspan=2)
submit_button = tk.Button(button_frame, text="Submit", command=submit)
submit_button.pack(side=tk.LEFT)
clear_button = tk.Button(button_frame, text="Clear", command=clear)
clear_button.pack(side=tk.LEFT)
result_button = tk.Button(button_frame, text="Result", command=lambda: (root.clipboard_clear(), root.clipboard_append(generate_request())))
result_button.pack(side=tk.LEFT)
programs = ['DMMVR', 'Fiddler']
for i, program in enumerate(programs):
button = tk.Button(button_frame, text=program, command=lambda p=program: run_program(p))
button.pack(side=tk.LEFT)
save_name_label = tk.Label(root, text="Save name:")
save_name_label.grid(row=3, column=0)
save_name_entry = tk.Entry(root)
save_name_entry.grid(row=4, column=0)
save_name_entry.bind("<Button-3>", paste)
workdir_label = tk.Label(root, text="WorkDir:")
workdir_label.grid(row=3, column=1)
workdir_entry = tk.Entry(root)
workdir_entry.grid(row=4, column=1)
workdir_entry.bind("<Button-3>", paste)
workdir_entry.bind("<FocusIn>", workdir_focus_in)
workdir_entry.bind("<FocusOut>", workdir_focus_out)
workdir_label = tk.Label(workdir_entry, text='当前目录', foreground='grey')
workdir_label.pack(fill='both', expand=True)
disable_date_info = tk.BooleanVar()
disable_date_info.set(True)
disable_date_info_checkbutton = tk.Checkbutton(root, text="disableDateInfo", variable=disable_date_info)
disable_date_info_checkbutton.grid(row=5, column=0, sticky=tk.W, padx=(33, 0))
enable_del_after_done = tk.BooleanVar()
enable_del_after_done.set(True)
enable_del_after_done_checkbutton = tk.Checkbutton(root, text="enableDelAfterDone", variable=enable_del_after_done)
enable_del_after_done_checkbutton.grid(row=5, column=1)
use_headers = tk.BooleanVar()
use_headers.set(False) # Default is not checked
headers_checkbutton = tk.Checkbutton(root, text="headers", variable=use_headers)
headers_checkbutton.grid(row=6, column=0, sticky=tk.W, padx=(33, 0))
load_config()
root.mainloop()
我没看出来问题,但是我尝试后发现方法没有失效,另外我现在是把整个伪装头加入参数的。你可以试试这个代码,我利用ChatGPT 3.5编写的GUI。我的python环境为3.11。把它保存在下载器的根目录,另外创建DMMVR和FD的快捷方式,也放到下载器根目录,命名为DMMVR, Fiddler,然后就可以运行脚本了。(第三方编译器可能会出错,最好在原生环境下运行,我debug 搞了半天,和GPT斗智斗勇,发现是解释器有毛病...)
运行后有两个输入框,上为输入链接,下为输入文件名(右键可以自动粘贴)
逻辑和之前一样,把digital的那一段复制下来,但是现在我们复制全部,即从GET到Alive。 复制粘贴后点击Submit,不出意外的话会唤起下载器进行下载。关于参数的三个选项参见下载器的解释。点击result可以查看生成的链接。FD显示链接失效后(404)关闭窗口,点击Clear,再重复上述步骤即可。链接状态码200却报红的话建议更换节点试试。 其中DMMVR, Fiddler两个按钮点击即启动对应程序,这在更新链接时比较方便。
另外并非只有日本本土IP可以下载,获取下载链接启动下载器解析链接后,部分非jp节点也有速度。具体机制尚未探明,可以确定的是jp节点更加稳定。
在 fidder 获取视频信息的请求postman,我失敗了(Could not send request ) 是不是需要Authorization: Bearer {{ token }}??
你好,我根据你的方法进行下载 但是和你一样下载不了 但是如果挂了梯子,就会得到这个结果,一直卡在0bytes/s 到底哪里出问题了呢,纯小白第一次用fidder
import os import tkinter as tk CONFIG_PATH = "save_config.txt" formatted_request = "" # Declare it as a global variable def format_request(request, save_name, workdir): request_line, headers = request.split('\n', 1) method, url, _ = request_line.split(' ', 2) headers = headers.split('\n') headers = '|'.join(h for h in headers if h) result = f"\"{url}\" --saveName \"{save_name}\"" # If the headers checkbox is checked, add headers to the result if use_headers.get(): result += f" --headers \"{method} {url} HTTP/1.1|{headers}\"" if workdir: result += f" --workDir \"{workdir}\"" return result def generate_request(): """Generates and returns the formatted request.""" global formatted_request # Use the global variable raw_request = request_entry.get('1.0', tk.END) save_name = save_name_entry.get() workdir = workdir_entry.get() if workdir_entry.get() != '当前目录' else '' formatted_request = format_request(raw_request, save_name, workdir) if enable_del_after_done.get(): formatted_request += " --enableDelAfterDone" if disable_date_info.get(): formatted_request += " --disableDateInfo" return formatted_request def submit(): request = generate_request() os.system(f"start cmd /k N_m3u8DL-CLI_v3.0.2.exe {request}") # Use os.system instead with open(CONFIG_PATH, 'w') as f: f.write(f"{save_name_entry.get()}\n{workdir_entry.get()}\n") def run_program(path): os.startfile(path) def paste(event): content = root.clipboard_get() event.widget.insert('insert', content) def clear(): request_entry.delete('1.0', tk.END) def clear_entry(event): event.widget.delete(0, tk.END) def workdir_focus_in(event): workdir_label.pack_forget() def workdir_focus_out(event): if not workdir_entry.get(): workdir_label.pack(fill='both', expand=True) def load_config(): if os.path.exists(CONFIG_PATH): with open(CONFIG_PATH, 'r') as f: lines = f.readlines() if len(lines) > 0: save_name_entry.insert(0, lines[0].strip()) if len(lines) > 1: workdir_entry.insert(0, lines[1].strip()) if lines[1].strip(): workdir_label.pack_forget() root = tk.Tk() root.title("Request Formatter") root.resizable(False, False) root.attributes('-topmost', True) window_width = 380 window_height = 420 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() position_top = int(screen_height / 2 - window_height / 2) position_right = int(screen_width / 2 - window_width / 2) root.geometry(f"{window_width}x{window_height}+{position_right}+{position_top}") request_label = tk.Label(root, text="HTTP GET request:") request_label.grid(row=0, column=0, columnspan=2) request_entry = tk.Text(root, height=19, width=50) request_entry.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 5)) request_entry.bind("<Button-3>", paste) button_frame = tk.Frame(root) button_frame.grid(row=2, column=0, columnspan=2) submit_button = tk.Button(button_frame, text="Submit", command=submit) submit_button.pack(side=tk.LEFT) clear_button = tk.Button(button_frame, text="Clear", command=clear) clear_button.pack(side=tk.LEFT) result_button = tk.Button(button_frame, text="Result", command=lambda: (root.clipboard_clear(), root.clipboard_append(generate_request()))) result_button.pack(side=tk.LEFT) programs = ['DMMVR', 'Fiddler'] for i, program in enumerate(programs): button = tk.Button(button_frame, text=program, command=lambda p=program: run_program(p)) button.pack(side=tk.LEFT) save_name_label = tk.Label(root, text="Save name:") save_name_label.grid(row=3, column=0) save_name_entry = tk.Entry(root) save_name_entry.grid(row=4, column=0) save_name_entry.bind("<Button-3>", paste) workdir_label = tk.Label(root, text="WorkDir:") workdir_label.grid(row=3, column=1) workdir_entry = tk.Entry(root) workdir_entry.grid(row=4, column=1) workdir_entry.bind("<Button-3>", paste) workdir_entry.bind("<FocusIn>", workdir_focus_in) workdir_entry.bind("<FocusOut>", workdir_focus_out) workdir_label = tk.Label(workdir_entry, text='当前目录', foreground='grey') workdir_label.pack(fill='both', expand=True) disable_date_info = tk.BooleanVar() disable_date_info.set(True) disable_date_info_checkbutton = tk.Checkbutton(root, text="disableDateInfo", variable=disable_date_info) disable_date_info_checkbutton.grid(row=5, column=0, sticky=tk.W, padx=(33, 0)) enable_del_after_done = tk.BooleanVar() enable_del_after_done.set(True) enable_del_after_done_checkbutton = tk.Checkbutton(root, text="enableDelAfterDone", variable=enable_del_after_done) enable_del_after_done_checkbutton.grid(row=5, column=1) use_headers = tk.BooleanVar() use_headers.set(False) # Default is not checked headers_checkbutton = tk.Checkbutton(root, text="headers", variable=use_headers) headers_checkbutton.grid(row=6, column=0, sticky=tk.W, padx=(33, 0)) load_config() root.mainloop()
我没看出来问题,但是我尝试后发现方法没有失效,另外我现在是把整个伪装头加入参数的。你可以试试这个代码,我利用ChatGPT 3.5编写的GUI。我的python环境为3.11。把它保存在下载器的根目录,另外创建DMMVR和FD的快捷方式,也放到下载器根目录,命名为DMMVR, Fiddler,然后就可以运行脚本了。(第三方编译器可能会出错,最好在原生环境下运行,我debug 搞了半天,和GPT斗智斗勇,发现是解释器有毛病...)
运行后有两个输入框,上为输入链接,下为输入文件名(右键可以自动粘贴)
逻辑和之前一样,把digital的那一段复制下来,但是现在我们复制全部,即从GET到Alive。 复制粘贴后点击Submit,不出意外的话会唤起下载器进行下载。关于参数的三个选项参见下载器的解释。点击result可以查看生成的链接。FD显示链接失效后(404)关闭窗口,点击Clear,再重复上述步骤即可。链接状态码200却报红的话建议更换节点试试。 其中DMMVR, Fiddler两个按钮点击即启动对应程序,这在更新链接时比较方便。
另外并非只有日本本土IP可以下载,获取下载链接启动下载器解析链接后,部分非jp节点也有速度。具体机制尚未探明,可以确定的是jp节点更加稳定。
感谢脚本分享,我从dmm打开流媒体传输几乎都是404,试过换节点,但是我按照你提供的方法成功了一次,那次恰好可以正常从链接播放视频,这种情况是网络的问题吗?
另外我无意间发现Fiddler抓取播放器播放下载到本地的wsdcf文件时,idm会提示能下载MP4格式的视频
但是会显示http服务器无响应
由于我对专业知识一窍不通,想知道这是否是一种有效的视频下载办法
感谢,今天测试下来下载没问题
建议想抓包的,直接用上面提供的py脚本,只需用Fiddler,按上面说的无脑复制对应信息进去,即可正常下载,2024年6月亲测,还能正常下载
建议想抓包的,直接用上面提供的py脚本,只需用Fiddler,按上面说的无脑复制对应信息进去,即可正常下载,2024年6月亲测,还能正常下载
那如果想发包呢?
建议想抓包的,直接用上面提供的py脚本,只需用Fiddler,按上面说的无脑复制信息进去,即可正常下载,2024年6月亲测,还能正常下载
2024/06/28测试已经不行了,在线播放都放不出来,日本、美国的梯子都不行,我问了问dmm的客服,他说用了vpn好像不能在线播放了,fidder监测只出现一个service和digital,即使把digital里的连接复制dl下载也没有反应
你好,我根据你的方法进行下载但是和你一样下载不了但是如果挂了梯子,就会得到这个结果,一直卡在0bytes/s 到底哪里出问题了呢,纯小白第一次用fidder
import os import tkinter as tk CONFIG_PATH = "save_config.txt" formatted_request = "" # Declare it as a global variable def format_request(request, save_name, workdir): request_line, headers = request.split('\n', 1) method, url, _ = request_line.split(' ', 2) headers = headers.split('\n') headers = '|'.join(h for h in headers if h) result = f"\"{url}\" --saveName \"{save_name}\"" # If the headers checkbox is checked, add headers to the result if use_headers.get(): result += f" --headers \"{method} {url} HTTP/1.1|{headers}\"" if workdir: result += f" --workDir \"{workdir}\"" return result def generate_request(): """Generates and returns the formatted request.""" global formatted_request # Use the global variable raw_request = request_entry.get('1.0', tk.END) save_name = save_name_entry.get() workdir = workdir_entry.get() if workdir_entry.get() != '当前目录' else '' formatted_request = format_request(raw_request, save_name, workdir) if enable_del_after_done.get(): formatted_request += " --enableDelAfterDone" if disable_date_info.get(): formatted_request += " --disableDateInfo" return formatted_request def submit(): request = generate_request() os.system(f"start cmd /k N_m3u8DL-CLI_v3.0.2.exe {request}") # Use os.system instead with open(CONFIG_PATH, 'w') as f: f.write(f"{save_name_entry.get()}\n{workdir_entry.get()}\n") def run_program(path): os.startfile(path) def paste(event): content = root.clipboard_get() event.widget.insert('insert', content) def clear(): request_entry.delete('1.0', tk.END) def clear_entry(event): event.widget.delete(0, tk.END) def workdir_focus_in(event): workdir_label.pack_forget() def workdir_focus_out(event): if not workdir_entry.get(): workdir_label.pack(fill='both', expand=True) def load_config(): if os.path.exists(CONFIG_PATH): with open(CONFIG_PATH, 'r') as f: lines = f.readlines() if len(lines) > 0: save_name_entry.insert(0, lines[0].strip()) if len(lines) > 1: workdir_entry.insert(0, lines[1].strip()) if lines[1].strip(): workdir_label.pack_forget() root = tk.Tk() root.title("Request Formatter") root.resizable(False, False) root.attributes('-topmost', True) window_width = 380 window_height = 420 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() position_top = int(screen_height / 2 - window_height / 2) position_right = int(screen_width / 2 - window_width / 2) root.geometry(f"{window_width}x{window_height}+{position_right}+{position_top}") request_label = tk.Label(root, text="HTTP GET request:") request_label.grid(row=0, column=0, columnspan=2) request_entry = tk.Text(root, height=19, width=50) request_entry.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 5)) request_entry.bind("<Button-3>", paste) button_frame = tk.Frame(root) button_frame.grid(row=2, column=0, columnspan=2) submit_button = tk.Button(button_frame, text="Submit", command=submit) submit_button.pack(side=tk.LEFT) clear_button = tk.Button(button_frame, text="Clear", command=clear) clear_button.pack(side=tk.LEFT) result_button = tk.Button(button_frame, text="Result", command=lambda: (root.clipboard_clear(), root.clipboard_append(generate_request()))) result_button.pack(side=tk.LEFT) programs = ['DMMVR', 'Fiddler'] for i, program in enumerate(programs): button = tk.Button(button_frame, text=program, command=lambda p=program: run_program(p)) button.pack(side=tk.LEFT) save_name_label = tk.Label(root, text="Save name:") save_name_label.grid(row=3, column=0) save_name_entry = tk.Entry(root) save_name_entry.grid(row=4, column=0) save_name_entry.bind("<Button-3>", paste) workdir_label = tk.Label(root, text="WorkDir:") workdir_label.grid(row=3, column=1) workdir_entry = tk.Entry(root) workdir_entry.grid(row=4, column=1) workdir_entry.bind("<Button-3>", paste) workdir_entry.bind("<FocusIn>", workdir_focus_in) workdir_entry.bind("<FocusOut>", workdir_focus_out) workdir_label = tk.Label(workdir_entry, text='当前目录', foreground='grey') workdir_label.pack(fill='both', expand=True) disable_date_info = tk.BooleanVar() disable_date_info.set(True) disable_date_info_checkbutton = tk.Checkbutton(root, text="disableDateInfo", variable=disable_date_info) disable_date_info_checkbutton.grid(row=5, column=0, sticky=tk.W, padx=(33, 0)) enable_del_after_done = tk.BooleanVar() enable_del_after_done.set(True) enable_del_after_done_checkbutton = tk.Checkbutton(root, text="enableDelAfterDone", variable=enable_del_after_done) enable_del_after_done_checkbutton.grid(row=5, column=1) use_headers = tk.BooleanVar() use_headers.set(False) # Default is not checked headers_checkbutton = tk.Checkbutton(root, text="headers", variable=use_headers) headers_checkbutton.grid(row=6, column=0, sticky=tk.W, padx=(33, 0)) load_config() root.mainloop()
我没有看到有任何疑问,但是我尝试后发现方法没有失效,另外我现在是把整个伪装头加入参数的。你可以试试这个代码,我利用ChatGPT 3.5编写的GUI。我的python环境为3.11。它将保存在下载器的根目录,另外创建DMMVR和FD的快捷方式,也放到下载器根目录,命名为DMMVR、Fiddler,然后就可以运行脚本了。(第三方编译器可能会出错,最好在原生环境下运行,我debug搞了半天,和GPT斗智斗勇,发现是解释器有毛病...)
运行后 这两个输入框,上为输入链接,下为输入文件名(右键可以自动粘贴)
逻辑和之前一样,把digital的那一段复制下来,但是现在我们复制全部,即从GET到Alive。 复制粘贴后点击Submit,不出意外的话会唤起下载器进行下载。关于参数的三个选项参见下载器的解释。点击结果可以查看生成的链接。FD显示链接失效后(404)关闭,点击Clear,再重复上述步骤即可。链接状态码200却报红的话建议更换节点试试。 其中DMMVR, Fiddler两个按钮点击即启动对应程序,这在更新链接时比较方便。
另外并非本土IP可以下载,获取下载链接启动器解析链接后,部分非jp节点也有所变化。具体机制尚不明确,可以确定的是jp节点更加稳定。
抱歉大佬,再次打扰,现在似乎无法用以前的方法下载了,最近已经没办法进行在线播放,我更换了多个jp的节点甚至其他地区的节点都没办法,我咨询了dmm的客服,他说使用vpn可能无法在线播放,使用fidder只能获得一个service和digital 然后就不动了,不会继续获取第二个digital,dmmvr播放器也根本没有播放,即使我强行使用digital里的链接复制到dl下载也只会一直显示开始解析,再没有后续反应
你好,我根据你的方法进行下载 但是和你一样下载不了 但是如果挂了梯子,就会得到这个结果,一直卡在0bytes/s 到底哪里出问题了呢,纯小白第一次用fidder
import os import tkinter as tk CONFIG_PATH = "save_config.txt" formatted_request = "" # Declare it as a global variable def format_request(request, save_name, workdir): request_line, headers = request.split('\n', 1) method, url, _ = request_line.split(' ', 2) headers = headers.split('\n') headers = '|'.join(h for h in headers if h) result = f"\"{url}\" --saveName \"{save_name}\"" # If the headers checkbox is checked, add headers to the result if use_headers.get(): result += f" --headers \"{method} {url} HTTP/1.1|{headers}\"" if workdir: result += f" --workDir \"{workdir}\"" return result def generate_request(): """Generates and returns the formatted request.""" global formatted_request # Use the global variable raw_request = request_entry.get('1.0', tk.END) save_name = save_name_entry.get() workdir = workdir_entry.get() if workdir_entry.get() != '当前目录' else '' formatted_request = format_request(raw_request, save_name, workdir) if enable_del_after_done.get(): formatted_request += " --enableDelAfterDone" if disable_date_info.get(): formatted_request += " --disableDateInfo" return formatted_request def submit(): request = generate_request() os.system(f"start cmd /k N_m3u8DL-CLI_v3.0.2.exe {request}") # Use os.system instead with open(CONFIG_PATH, 'w') as f: f.write(f"{save_name_entry.get()}\n{workdir_entry.get()}\n") def run_program(path): os.startfile(path) def paste(event): content = root.clipboard_get() event.widget.insert('insert', content) def clear(): request_entry.delete('1.0', tk.END) def clear_entry(event): event.widget.delete(0, tk.END) def workdir_focus_in(event): workdir_label.pack_forget() def workdir_focus_out(event): if not workdir_entry.get(): workdir_label.pack(fill='both', expand=True) def load_config(): if os.path.exists(CONFIG_PATH): with open(CONFIG_PATH, 'r') as f: lines = f.readlines() if len(lines) > 0: save_name_entry.insert(0, lines[0].strip()) if len(lines) > 1: workdir_entry.insert(0, lines[1].strip()) if lines[1].strip(): workdir_label.pack_forget() root = tk.Tk() root.title("Request Formatter") root.resizable(False, False) root.attributes('-topmost', True) window_width = 380 window_height = 420 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() position_top = int(screen_height / 2 - window_height / 2) position_right = int(screen_width / 2 - window_width / 2) root.geometry(f"{window_width}x{window_height}+{position_right}+{position_top}") request_label = tk.Label(root, text="HTTP GET request:") request_label.grid(row=0, column=0, columnspan=2) request_entry = tk.Text(root, height=19, width=50) request_entry.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 5)) request_entry.bind("<Button-3>", paste) button_frame = tk.Frame(root) button_frame.grid(row=2, column=0, columnspan=2) submit_button = tk.Button(button_frame, text="Submit", command=submit) submit_button.pack(side=tk.LEFT) clear_button = tk.Button(button_frame, text="Clear", command=clear) clear_button.pack(side=tk.LEFT) result_button = tk.Button(button_frame, text="Result", command=lambda: (root.clipboard_clear(), root.clipboard_append(generate_request()))) result_button.pack(side=tk.LEFT) programs = ['DMMVR', 'Fiddler'] for i, program in enumerate(programs): button = tk.Button(button_frame, text=program, command=lambda p=program: run_program(p)) button.pack(side=tk.LEFT) save_name_label = tk.Label(root, text="Save name:") save_name_label.grid(row=3, column=0) save_name_entry = tk.Entry(root) save_name_entry.grid(row=4, column=0) save_name_entry.bind("<Button-3>", paste) workdir_label = tk.Label(root, text="WorkDir:") workdir_label.grid(row=3, column=1) workdir_entry = tk.Entry(root) workdir_entry.grid(row=4, column=1) workdir_entry.bind("<Button-3>", paste) workdir_entry.bind("<FocusIn>", workdir_focus_in) workdir_entry.bind("<FocusOut>", workdir_focus_out) workdir_label = tk.Label(workdir_entry, text='当前目录', foreground='grey') workdir_label.pack(fill='both', expand=True) disable_date_info = tk.BooleanVar() disable_date_info.set(True) disable_date_info_checkbutton = tk.Checkbutton(root, text="disableDateInfo", variable=disable_date_info) disable_date_info_checkbutton.grid(row=5, column=0, sticky=tk.W, padx=(33, 0)) enable_del_after_done = tk.BooleanVar() enable_del_after_done.set(True) enable_del_after_done_checkbutton = tk.Checkbutton(root, text="enableDelAfterDone", variable=enable_del_after_done) enable_del_after_done_checkbutton.grid(row=5, column=1) use_headers = tk.BooleanVar() use_headers.set(False) # Default is not checked headers_checkbutton = tk.Checkbutton(root, text="headers", variable=use_headers) headers_checkbutton.grid(row=6, column=0, sticky=tk.W, padx=(33, 0)) load_config() root.mainloop()
我没看出来问题,但是我尝试后发现方法没有失效,另外我现在是把整个伪装头加入参数的。你可以试试这个代码,我利用ChatGPT 3.5编写的GUI。我的python环境为3.11。把它保存在下载器的根目录,另外创建DMMVR和FD的快捷方式,也放到下载器根目录,命名为DMMVR, Fiddler,然后就可以运行脚本了。(第三方编译器可能会出错,最好在原生环境下运行,我debug 搞了半天,和GPT斗智斗勇,发现是解释器有毛病...)
运行后有两个输入框,上为输入链接,下为输入文件名(右键可以自动粘贴)
逻辑和之前一样,把digital的那一段复制下来,但是现在我们复制全部,即从GET到Alive。 复制粘贴后点击Submit,不出意外的话会唤起下载器进行下载。关于参数的三个选项参见下载器的解释。点击result可以查看生成的链接。FD显示链接失效后(404)关闭窗口,点击Clear,再重复上述步骤即可。链接状态码200却报红的话建议更换节点试试。 其中DMMVR, Fiddler两个按钮点击即启动对应程序,这在更新链接时比较方便。
另外并非只有日本本土IP可以下载,获取下载链接启动下载器解析链接后,部分非jp节点也有速度。具体机制尚未探明,可以确定的是jp节点更加稳定。
兄弟们,好消息,我找到一个新的下载方法,https://tieba.baidu.com/p/9068142085,要不怎么说贴吧人才多,这个软件github也能找到,跟咱们原来的方法不同,它不需要抓取链接,而是需要下载的wsdcf的加密视频文件,这个软件也要梯子,也得调用dmmvr播放器,不知道是直接破解,还是在播放本地文件时与服务器通讯时获取了什么链接,但是我机场的流量监测没有变化,什么原理呢。。。
建议想抓包的,直接用上面提供的py脚本,只需用Fiddler,按上面说的无脑复制信息进去,即可正常下载,2024年6月亲测,还能正常下载
2024/06/28测试已经不行了,在线播放都放不出来,日本、美国的梯子都不行,我问了问dmm的客服,他说用vpn好像不能在线播放了,fidder监测只出现一个服务和digital,即使把digital里的连接复制dl下载也没有反应 还是可以抓取get下载的,操作没有问题的话,纯就是节点问题,换个质量高点的日本节点,最好原始或者家宽的,我刚刚买了一部没有放源的VR片,按老流程刚好下载完一个
你好,我根据你的方法进行下载 但是和你一样下载不了 但是如果挂了梯子,就会得到这个结果,一直卡在0bytes/s 到底哪里出问题了呢,纯小白第一次用fidder
import os import tkinter as tk CONFIG_PATH = "save_config.txt" formatted_request = "" # Declare it as a global variable def format_request(request, save_name, workdir): request_line, headers = request.split('\n', 1) method, url, _ = request_line.split(' ', 2) headers = headers.split('\n') headers = '|'.join(h for h in headers if h) result = f"\"{url}\" --saveName \"{save_name}\"" # If the headers checkbox is checked, add headers to the result if use_headers.get(): result += f" --headers \"{method} {url} HTTP/1.1|{headers}\"" if workdir: result += f" --workDir \"{workdir}\"" return result def generate_request(): """Generates and returns the formatted request.""" global formatted_request # Use the global variable raw_request = request_entry.get('1.0', tk.END) save_name = save_name_entry.get() workdir = workdir_entry.get() if workdir_entry.get() != '当前目录' else '' formatted_request = format_request(raw_request, save_name, workdir) if enable_del_after_done.get(): formatted_request += " --enableDelAfterDone" if disable_date_info.get(): formatted_request += " --disableDateInfo" return formatted_request def submit(): request = generate_request() os.system(f"start cmd /k N_m3u8DL-CLI_v3.0.2.exe {request}") # Use os.system instead with open(CONFIG_PATH, 'w') as f: f.write(f"{save_name_entry.get()}\n{workdir_entry.get()}\n") def run_program(path): os.startfile(path) def paste(event): content = root.clipboard_get() event.widget.insert('insert', content) def clear(): request_entry.delete('1.0', tk.END) def clear_entry(event): event.widget.delete(0, tk.END) def workdir_focus_in(event): workdir_label.pack_forget() def workdir_focus_out(event): if not workdir_entry.get(): workdir_label.pack(fill='both', expand=True) def load_config(): if os.path.exists(CONFIG_PATH): with open(CONFIG_PATH, 'r') as f: lines = f.readlines() if len(lines) > 0: save_name_entry.insert(0, lines[0].strip()) if len(lines) > 1: workdir_entry.insert(0, lines[1].strip()) if lines[1].strip(): workdir_label.pack_forget() root = tk.Tk() root.title("Request Formatter") root.resizable(False, False) root.attributes('-topmost', True) window_width = 380 window_height = 420 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() position_top = int(screen_height / 2 - window_height / 2) position_right = int(screen_width / 2 - window_width / 2) root.geometry(f"{window_width}x{window_height}+{position_right}+{position_top}") request_label = tk.Label(root, text="HTTP GET request:") request_label.grid(row=0, column=0, columnspan=2) request_entry = tk.Text(root, height=19, width=50) request_entry.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 5)) request_entry.bind("<Button-3>", paste) button_frame = tk.Frame(root) button_frame.grid(row=2, column=0, columnspan=2) submit_button = tk.Button(button_frame, text="Submit", command=submit) submit_button.pack(side=tk.LEFT) clear_button = tk.Button(button_frame, text="Clear", command=clear) clear_button.pack(side=tk.LEFT) result_button = tk.Button(button_frame, text="Result", command=lambda: (root.clipboard_clear(), root.clipboard_append(generate_request()))) result_button.pack(side=tk.LEFT) programs = ['DMMVR', 'Fiddler'] for i, program in enumerate(programs): button = tk.Button(button_frame, text=program, command=lambda p=program: run_program(p)) button.pack(side=tk.LEFT) save_name_label = tk.Label(root, text="Save name:") save_name_label.grid(row=3, column=0) save_name_entry = tk.Entry(root) save_name_entry.grid(row=4, column=0) save_name_entry.bind("<Button-3>", paste) workdir_label = tk.Label(root, text="WorkDir:") workdir_label.grid(row=3, column=1) workdir_entry = tk.Entry(root) workdir_entry.grid(row=4, column=1) workdir_entry.bind("<Button-3>", paste) workdir_entry.bind("<FocusIn>", workdir_focus_in) workdir_entry.bind("<FocusOut>", workdir_focus_out) workdir_label = tk.Label(workdir_entry, text='当前目录', foreground='grey') workdir_label.pack(fill='both', expand=True) disable_date_info = tk.BooleanVar() disable_date_info.set(True) disable_date_info_checkbutton = tk.Checkbutton(root, text="disableDateInfo", variable=disable_date_info) disable_date_info_checkbutton.grid(row=5, column=0, sticky=tk.W, padx=(33, 0)) enable_del_after_done = tk.BooleanVar() enable_del_after_done.set(True) enable_del_after_done_checkbutton = tk.Checkbutton(root, text="enableDelAfterDone", variable=enable_del_after_done) enable_del_after_done_checkbutton.grid(row=5, column=1) use_headers = tk.BooleanVar() use_headers.set(False) # Default is not checked headers_checkbutton = tk.Checkbutton(root, text="headers", variable=use_headers) headers_checkbutton.grid(row=6, column=0, sticky=tk.W, padx=(33, 0)) load_config() root.mainloop()
我没看出来问题,但是我尝试后发现方法没有失效,另外我现在是把整个伪装头加入参数的。你可以试试这个代码,我利用ChatGPT 3.5编写的GUI。我的python环境为3.11。把它保存在下载器的根目录,另外创建DMMVR和FD的快捷方式,也放到下载器根目录,命名为DMMVR, Fiddler,然后就可以运行脚本了。(第三方编译器可能会出错,最好在原生环境下运行,我debug 搞了半天,和GPT斗智斗勇,发现是解释器有毛病...) 运行后有两个输入框,上为输入链接,下为输入文件名(右键可以自动粘贴) 逻辑和之前一样,把digital的那一段复制下来,但是现在我们复制全部,即从GET到Alive。 复制粘贴后点击Submit,不出意外的话会唤起下载器进行下载。关于参数的三个选项参见下载器的解释。点击result可以查看生成的链接。FD显示链接失效后(404)关闭窗口,点击Clear,再重复上述步骤即可。链接状态码200却报红的话建议更换节点试试。 其中DMMVR, Fiddler两个按钮点击即启动对应程序,这在更新链接时比较方便。 另外并非只有日本本土IP可以下载,获取下载链接启动下载器解析链接后,部分非jp节点也有速度。具体机制尚未探明,可以确定的是jp节点更加稳定。
兄弟,能发一下github链接吗?贴吧帖子没了
看到之前有issue #473 讨论过DMM视频的下载,但是关于VR视频如何下载并没有结论,今天抓包研究了一下,和普通的2D视频流程不太一样 环境:windows10,N_m3u8DL-CLI_v3.0.1
前置条件:
流程:
dmmvrplayerstreaming:digital?product_id={{product_id}}&quality=auto&shop=videoa&parent_product_id={{parent_product_id}}&past_st_flag=0&past_dl_flag=0&quality_groups=high&all_parts=1&part=1
的地址message=Digital_Api_PlayableProvider.getContentInfo¶ms={"transfer_type":"stream","quality":"auto","device":"pc_vr","vr_appli_type":"pc","adult_flag":false,"product_id":"{{product_id}}","exploit_id":"{{exploit_id}}","part":"0","shop":"videoa","HTTP_USER_AGENT":"WINDOWSVR_DMMVRPLAY 2.0.5 Intel(R) UHD Graphics 630","is_past":false,"parent_product_id":"{{parent_product_id}}","quality_groups":["high"]}&appid=pc_movievrplayer&authkey=cb0809a54a507acdd880369ea0df939dae4596822fd46fe51616164a2409c030
{ "event": true, "data": { "cookie": [ { "name": "licenseUID", "value": "{{licenseUID}}", "expire": 0, "path": "/", "domain": "dmm.com" } ], "content_id": "{{content_id}}", "content_title": "{{content_title}}", "content_info": { "normal": { "redirect": "https://str.dmm.com:443/digital/st1:etmvTFqzN6%2BYjb9582jo7gR7KUPc1p2YtHpxgU6Q1d3J-Rxj0wdtXmqYjilTIDyOYPjysXwfsPOSrFACOubTfsCZ4CKH1THt3GOANei5IBE%3D/2xcmMgca4dTbhr9s3WvW4Zo/-/playlist.m3u8?ld=NE6DLADu3aH%2BKnQLk%2BBCzYGCZBUXD9BU3PQy7k1LGnwDzHUn4mkhD%2BT1%2Bv2bJUBXMdqSdaEO0gluniWZUWMLwN%2BFpWuYc4%2BLg6y4gWYgMaE%3D&luid=cojp", "recommended_viewing_type": "3d_horizontal_180", "height": 1920, "width": 3840 } } }, "exectime": 0.4461, "memory": "4,229,576" }
message=Digital_Api_PlayableProvider.getContentInfo¶ms={"transfer_type":"stream","quality":"auto","device":"pc_vr","vr_appli_type":"pc","adult_flag":false,"product_id":"{{product_id}}","exploit_id":"{{exploit_id}}","part":"0","shop":"videoa","HTTP_USER_AGENT":"WINDOWSVR_DMMVRPLAY 2.0.5 Intel(R) UHD Graphics 630","is_past":false,"parent_product_id":"{{parent_product_id}}","quality_groups":["high"]}&appid=pc_movievrplayer&authkey=cb0809a54a507acdd880369ea0df939dae4596822fd46fe51616164a2409c030
message=Digital_Api_PlayableProvider.getContentInfo¶ms={"transfer_type":"stream","quality":"auto","device":"pc_vr","vr_appli_type":"pc","adult_flag":false,"product_id":"h_1155crvr00100zero","exploit_id":"uid:Y2ETW22TVYvDF1ls","part":"0","shop":"videoa","HTTP_USER_AGENT":"WINDOWSVR_DMMVRPLAY 2.0.5 Intel(R) UHD Graphics 630","is_past":false,"parent_product_id":"h_1155crvr00100zerodl6","quality_groups":["high"]}&appid=pc_movievrplayer&authkey=cb0809a54a507acdd880369ea0df939dae4596822fd46fe51616164a2409c030