diff --git a/README.md b/README.md index 6c53336..d9fac92 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ [简体中文](docs/Readme_zh-cn.md) # OneDriveShareLinkPushAria2 -Extract download URLs from OneDrive or SharePoint share links and push them to aria2, even on systems without a GUI. +Extract download URLs from OneDrive or SharePoint share links and push them to aria2, even on systems without a GUI (such as Linux). -从OneDrive或SharePoint共享链接提取下载URL并将其推送到aria2,即使在无图形界面的系统中依然可以使用。 +从OneDrive或SharePoint共享链接提取下载URL并将其推送到aria2,即使在无图形界面的系统中(如Linux)依然可以使用。 # Dependent @@ -20,7 +20,7 @@ At present, this program supports the following download methods: * Downloading multiple files with password for shared links * Download of files in nested folders * Download any file of your choice - * Viewing of more than 30 file lists (download mode planning) + * Viewing of more than 30 file lists * xxx.sharepoint.com Downloads with share links * xxx-my.sharepoint.cn Download of share links (theoretically supported) @@ -63,3 +63,5 @@ Usage is similar to the above. # Note Before you use it, clone the whole project with `git clone https://github.com/gaowanliang/OneDriveShareLinkPushAria2.git` to use it. havepassword.py depends on main.py, if you want to use the version that requires a password If you want to use a version that requires a password, you need to `pip install pyppeteer` + +The basic functions of this program have been realized. For a long time, if the software is not unusable, it will not be maintained. If there is a running problem, please bring a download link when raising the issue. The bug type issue that does not provide a download link will not be solved. \ No newline at end of file diff --git a/docs/Readme_zh-cn.md b/docs/Readme_zh-cn.md index 489b210..189c9bb 100644 --- a/docs/Readme_zh-cn.md +++ b/docs/Readme_zh-cn.md @@ -53,4 +53,6 @@ pyppeteer==0.2.5 使用方法和上面类似。 # 注意 -使用前,使用 `git clone https://github.com/gaowanliang/OneDriveShareLinkPushAria2.git` 将项目整个克隆,才能使用,havepassword.py依赖于main.py,如果要使用需要密码的版本,需要 `pip install pyppeteer` \ No newline at end of file +使用前,使用 `git clone https://github.com/gaowanliang/OneDriveShareLinkPushAria2.git` 将项目整个克隆,才能使用,havepassword.py依赖于main.py,如果要使用需要密码的版本,需要 `pip install pyppeteer` + +此程序基本功能都已实现,之后很长的一段时间内,如果不是软件无法使用了,则不再维护,如有运行问题,请在提出issue时带上下载链接,不提供下载链接的bug类型的issue将不会解决。 \ No newline at end of file diff --git a/main.py b/main.py index d2452ba..584fcca 100644 --- a/main.py +++ b/main.py @@ -10,7 +10,7 @@ import os import copy -OneDriveShareURL = "https://gw6-my.sharepoint.com/:f:/g/personal/admin_gwliang_com/EuVr3Xt3csJMhnBmmK1Yw7wBUH9VQ3UFHHPxkdFJhHRpVQ?e=CAQLWc" +OneDriveShareURL = "https://acgmi-my.sharepoint.com/:f:/g/personal/support_acgmi_club/EpSr_7JeS85CipoL4p0A_tgBe-mWXj3SCnWV1qihc29IpQ?e=8DpGN6" aria2Link = "http://localhost:6800/jsonrpc" aria2Secret = "123456" @@ -61,29 +61,34 @@ def getFiles(originalPath, req, layers, _id=0): urllib.parse.urlsplit(redirectURL).query)) redirectSplitURL = redirectURL.split("/") - relativeUrl = "" + relativeFolder = "" rootFolder = query["id"] for i in rootFolder.split("/"): if i != "Documents": - relativeUrl += i+"/" + relativeFolder += i+"/" else: - relativeUrl += i + relativeFolder += i break - relativeUrl = parse.quote(relativeUrl).replace("/", "%2F") - rootFolderUrl = parse.quote(rootFolder).replace("/", "%2F") + relativeUrl = parse.quote(relativeFolder).replace( + "/", "%2F").replace("_", "%5F").replace("-", "%2D") + rootFolderUrl = parse.quote(rootFolder).replace( + "/", "%2F").replace("_", "%5F").replace("-", "%2D") - graphqlVar = '{"query":"query (\n $listServerRelativeUrl: String!,$renderListDataAsStreamParameters: RenderListDataAsStreamParameters!,$renderListDataAsStreamQueryString: String!\n )\n {\n \n legacy {\n \n renderListDataAsStream(\n listServerRelativeUrl: $listServerRelativeUrl,\n parameters: $renderListDataAsStreamParameters,\n queryString: $renderListDataAsStreamQueryString\n )\n }\n \n \n perf {\n executionTime\n overheadTime\n parsingTime\n queryCount\n validationTime\n resolvers {\n name\n queryCount\n resolveTime\n waitTime\n }\n }\n }","variables":{"listServerRelativeUrl":"/personal/admin_gwliang_com/Documents","renderListDataAsStreamParameters":{"renderOptions":5707527,"allowMultipleValueFilterForTaxonomyFields":true,"addRequiredFields":true,"folderServerRelativeUrl":"%s"},"renderListDataAsStreamQueryString":"@a1=\'%s}\'&RootFolder=%s}&TryNewExperienceSingle=TRUE"}}' % (rootFolder, relativeUrl, rootFolderUrl) + graphqlVar = '{"query":"query (\n $listServerRelativeUrl: String!,$renderListDataAsStreamParameters: RenderListDataAsStreamParameters!,$renderListDataAsStreamQueryString: String!\n )\n {\n \n legacy {\n \n renderListDataAsStream(\n listServerRelativeUrl: $listServerRelativeUrl,\n parameters: $renderListDataAsStreamParameters,\n queryString: $renderListDataAsStreamQueryString\n )\n }\n \n \n perf {\n executionTime\n overheadTime\n parsingTime\n queryCount\n validationTime\n resolvers {\n name\n queryCount\n resolveTime\n waitTime\n }\n }\n }","variables":{"listServerRelativeUrl":"%s","renderListDataAsStreamParameters":{"renderOptions":5707527,"allowMultipleValueFilterForTaxonomyFields":true,"addRequiredFields":true,"folderServerRelativeUrl":"%s"},"renderListDataAsStreamQueryString":"@a1=\'%s\'&RootFolder=%s&TryNewExperienceSingle=TRUE"}}' % (relativeFolder, rootFolder, relativeUrl, rootFolderUrl) + # print(graphqlVar) s2 = urllib.parse.urlparse(redirectURL) tempHeader = copy.deepcopy(header) tempHeader["referer"] = redirectURL tempHeader["cookie"] = reqf.headers["set-cookie"] tempHeader["authority"] = s2.netloc tempHeader["content-type"] = "application/json;odata=verbose" + # print(redirectSplitURL) graphqlReq = req.post( "/".join(redirectSplitURL[:-3])+"/_api/v2.1/graphql", data=graphqlVar.encode('utf-8'), headers=tempHeader) graphqlReq = json.loads(graphqlReq.text) + # print(graphqlReq) if "NextHref" in graphqlReq["data"]["legacy"]["renderListDataAsStream"]["ListData"]: nextHref = graphqlReq[ "data"]["legacy"]["renderListDataAsStream"]["ListData"]["NextHref"]+"&@a1=%s&TryNewExperienceSingle=TRUE" % ( @@ -156,9 +161,7 @@ def downloadFiles(originalPath, req, layers, aria2URL, token, num=-1, _id=0, ori print("\t"*layers, "这个文件夹没有文件") return 0 - pat = re.search( - 'g_listData = {"wpq":"","Templates":{},"ListData":{ "Row" : ([\s\S]*?),"FirstRow"', reqf.text) - jsonData = json.loads(pat.group(1)) + filesData = [] redirectURL = reqf.url redirectSplitURL = redirectURL.split("/") query = dict(urllib.parse.parse_qsl( @@ -173,7 +176,7 @@ def downloadFiles(originalPath, req, layers, aria2URL, token, num=-1, _id=0, ori downloadURL.netloc, downloadURL.path).split("/") downloadURL = "/".join(downloadURL[:-1]) + \ "/download.aspx?UniqueId=" - print(downloadURL) + # print(downloadURL) # print(reqf.headers) @@ -190,9 +193,72 @@ def downloadFiles(originalPath, req, layers, aria2URL, token, num=-1, _id=0, ori # print(key+':'+str(value)) headerStr += key+':'+str(value)+"\n" + + relativeFolder = "" + rootFolder = query["id"] + for i in rootFolder.split("/"): + if i != "Documents": + relativeFolder += i+"/" + else: + relativeFolder += i + break + relativeUrl = parse.quote(relativeFolder).replace( + "/", "%2F").replace("_", "%5F").replace("-", "%2D") + rootFolderUrl = parse.quote(rootFolder).replace( + "/", "%2F").replace("_", "%5F").replace("-", "%2D") + + graphqlVar = '{"query":"query (\n $listServerRelativeUrl: String!,$renderListDataAsStreamParameters: RenderListDataAsStreamParameters!,$renderListDataAsStreamQueryString: String!\n )\n {\n \n legacy {\n \n renderListDataAsStream(\n listServerRelativeUrl: $listServerRelativeUrl,\n parameters: $renderListDataAsStreamParameters,\n queryString: $renderListDataAsStreamQueryString\n )\n }\n \n \n perf {\n executionTime\n overheadTime\n parsingTime\n queryCount\n validationTime\n resolvers {\n name\n queryCount\n resolveTime\n waitTime\n }\n }\n }","variables":{"listServerRelativeUrl":"%s","renderListDataAsStreamParameters":{"renderOptions":5707527,"allowMultipleValueFilterForTaxonomyFields":true,"addRequiredFields":true,"folderServerRelativeUrl":"%s"},"renderListDataAsStreamQueryString":"@a1=\'%s\'&RootFolder=%s&TryNewExperienceSingle=TRUE"}}' % (relativeFolder, rootFolder, relativeUrl, rootFolderUrl) + + # print(graphqlVar) + s2 = urllib.parse.urlparse(redirectURL) + tempHeader = copy.deepcopy(header) + tempHeader["referer"] = redirectURL + tempHeader["cookie"] = reqf.headers["set-cookie"] + tempHeader["authority"] = s2.netloc + tempHeader["content-type"] = "application/json;odata=verbose" + # print(redirectSplitURL) + + graphqlReq = req.post( + "/".join(redirectSplitURL[:-3])+"/_api/v2.1/graphql", data=graphqlVar.encode('utf-8'), headers=tempHeader) + graphqlReq = json.loads(graphqlReq.text) + # print(graphqlReq) + if "NextHref" in graphqlReq["data"]["legacy"]["renderListDataAsStream"]["ListData"]: + nextHref = graphqlReq[ + "data"]["legacy"]["renderListDataAsStream"]["ListData"]["NextHref"]+"&@a1=%s&TryNewExperienceSingle=TRUE" % ( + "%27"+relativeUrl+"%27") + filesData.extend(graphqlReq[ + "data"]["legacy"]["renderListDataAsStream"]["ListData"]["Row"]) + # print(filesData) + + listViewXml = graphqlReq[ + "data"]["legacy"]["renderListDataAsStream"]["ViewMetadata"]["ListViewXml"] + renderListDataAsStreamVar = '{"parameters":{"__metadata":{"type":"SP.RenderListDataParameters"},"RenderOptions":1216519,"ViewXml":"%s","AllowMultipleValueFilterForTaxonomyFields":true,"AddRequiredFields":true}}' % ( + listViewXml).replace('"', '\\"') + # print(renderListDataAsStreamVar, nextHref,1) + + # print(listViewXml) + + graphqlReq = req.post( + "/".join(redirectSplitURL[:-3])+"/_api/web/GetListUsingPath(DecodedUrl=@a1)/RenderListDataAsStream"+nextHref, data=renderListDataAsStreamVar.encode('utf-8'), headers=tempHeader) + graphqlReq = json.loads(graphqlReq.text) + # print(graphqlReq) + + while "NextHref" in graphqlReq["ListData"]: + nextHref = graphqlReq["ListData"]["NextHref"]+"&@a1=%s&TryNewExperienceSingle=TRUE" % ( + "%27"+relativeUrl+"%27") + filesData.extend(graphqlReq["ListData"]["Row"]) + graphqlReq = req.post( + "/".join(redirectSplitURL[:-3])+"/_api/web/GetListUsingPath(DecodedUrl=@a1)/RenderListDataAsStream"+nextHref, data=renderListDataAsStreamVar.encode('utf-8'), headers=tempHeader) + # print(graphqlReq.text) + graphqlReq = json.loads(graphqlReq.text) + # print(graphqlReq) + filesData.extend(graphqlReq["ListData"]["Row"]) + else: + filesData = filesData.extend(graphqlReq[ + "data"]["legacy"]["renderListDataAsStream"]["ListData"]["Row"]) + fileCount = 0 - # print(headerStr) - for i in jsonData: + for i in filesData: if i['FSObjType'] == "1": print("\t"*layers, "文件夹:", i['FileLeafRef'], "\t独特ID:", i["UniqueId"], "正在进入")