Greasy Fork is available in English.
专门用来汇出与汇入工作坊模组清单的脚本!
当前为
// ==UserScript==
// @name Steam 工作坊模组辅助脚本
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 专门用来汇出与汇入工作坊模组清单的脚本!
// @author LaysDragon镭锶龙
// @match https://steamcommunity.com/id/*/myworkshopfiles/*
// @require https://cdnjs.cloudflare.com/ajax/libs/URI.js/1.19.1/URI.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js
// @require https://code.jquery.com/jquery-3.3.1.min.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
window.URI = URI;
var $NJ = $;
window.$NJ = $;
$.noConflict()
//var URI = URI;
//var saveAs = saveAs;
if(typeof URI().query(true).appid === 'undefined' || URI().query(true).appid === "0"){
return;
}
//$NJ("head").append ('<link href="https://code.jquery.com/ui/1.12.1/themes/vader/jquery-ui.css" rel="stylesheet" type="text/css">');
$NJ('.rightDetailsBlock:eq(1)').prepend('<br>');
$NJ('.rightDetailsBlock:eq(1)').prepend($NJ('<span class="btn_green_white_innerfade btn_medium"> <span> 汇入模组清单 </span> </span>').click(startImport));
$NJ('.rightDetailsBlock:eq(1)').prepend($NJ('<span class="btn_green_white_innerfade btn_medium" style="margin:0px 10px 10px 0px"> <span> 汇出模组清单 </span> </span>').click(startExport));
$NJ('body').append($NJ(`
<form class="smallForm" method="POST" name="PublishedFileSubscribe" id="PublishedFileSubscribe" action="https://steamcommunity.com/sharedfiles/subscribe">
<input type="hidden" name="id" value="">
<input type="hidden" name="appid" value="">
<input type="hidden" name="sessionid" value="${getSessionID()}">
</form>
`))
function getSessionID(){
return $NJ('form#PublishedFileUnsubscribe input[name="sessionid"]').val()
}
function getPageList(){
return $NJ('[id^=Subscription]').map(function(){
let ele = $NJ(this);
//https://steamcommunity.com/sharedfiles/filedetails/?id=
return {
name:ele.find('.workshopItemTitle').text(),
//link:ele.find('a:has(div.workshopItemTitle)').attr('href'),
id:URI(ele.find('a:has(div.workshopItemTitle)').attr('href')).query(true).id,
//img:ele.find('img.workshopItemPreviewImage').attr('src')
}
}).get();
}
function currentMaxItems(){
let all = new Set(['10','20','30']);
let items = new Set($NJ('.workshopBrowsePaging div:eq(1) a').map(function(){return $NJ(this).text()}).get());
let target = [...all].filter(x => !items.has(x));
return target[0];
}
function selectMaxItems(value){
let all = new Set(['10','20','30']);
if(!all.has(value)){
throw new Error('Invalid Select Option');
}
$NJ
(`.workshopBrowsePaging div:eq(1) a:contains("${value}")`)[0].click();
}
function hasNextPage(){
let nextPageButton = $NJ('.workshopBrowsePagingControls:first').find('span:contains(">"),a:contains(">")');
return nextPageButton.length!=0 && !nextPageButton.hasClass('disabled');
}
function goNextPage(){
$NJ('.workshopBrowsePagingControls:first').find('span:contains(">"),a:contains(">")')[0].click();
}
//status: STOP PROCESSING
function getStatus(){
return localStorage.getItem('exporterStatus')||"STOP";
}
let allFlags = new Set(['TASK_CLEAR_IMPORT']);
function setFlag(flag){
if(!allFlags.has(flag)){
throw new Error('Invalid Flag');
}
localStorage.setItem('exporterFlag_'+flag,true);
}
function clearFlag(flag){
if(!allFlags.has(flag)){
throw new Error('Invalid Flag');
}
localStorage.setItem('exporterFlag_'+flag,false);
}
function getFlag(flag){
if(!allFlags.has(flag)){
throw new Error('Invalid Flag');
}
return JSON.parse(localStorage.getItem('exporterFlag_'+flag)||'false');
}
function getAppID(){
return URI().query(true).appid;
}
function getAppName(){
return $NJ
('.HeaderUserInfoSection:last').text();
}
function getUserID(){
return URI().segment(1);
}
function setStatus(status){
let all = new Set(['STOP','PROCESSING']);
if(!all.has(status)){
throw new Error('Invalid Status');
}
localStorage.setItem('exporterStatus',status);
}
function getListData(){
return JSON.parse(localStorage.getItem('exporterLists')||'[]');
}
function setListData(data){
localStorage.setItem('exporterLists',JSON.stringify(data));
}
function getImportListData(){
return JSON.parse(localStorage.getItem('exporterImportLists')||'[]');
}
function setImportListData(data){
localStorage.setItem('exporterImportLists',JSON.stringify(data));
}
function startExport(){
//$NJ( "#export_notification_dialog" ).dialog();
ShowBlockingWaitDialog('汇出MOD清单','脚本正在进行模组资料收集作业,请稍后...');
if(getStatus()=="STOP"){
setStatus('PROCESSING');
setListData([]);
location.href = URI().search(function(data) {
data.p = 1;
return data;
}).toString();
}
}
function startImport(){
{
let $Body = $NJ('<div/>');
$Body.append( $NJ(`<p>请填入存档字串或者JSON字串</p>`));
let $TextArea = $NJ('<textarea/>' , { 'class': 'newmodal_prompt_textarea' } );
$Body.append( $NJ('<div/>', {'class': 'newmodal_prompt_with_textarea gray_bevel fullwidth ' } ).append( $TextArea ) );
var deferred = new jQuery.Deferred();
var fnOK = function() { deferred.resolve( $TextArea.val() ); };
var fnCancel = function() { deferred.reject(); };
let $okButton = $NJ('<button/>', {type: 'submit', 'class': 'btn_green_white_innerfade btn_medium' } ).append( $NJ( '<span/>' ).text( '确定' ) );
$okButton.click( function(){
importing($TextArea.val());
Modal.Dismiss();
} );
deferred.always( function() { Modal.Dismiss(); } );
let $CancelButton = _BuildDialogButton( '取消' );
$CancelButton.click( fnCancel);
let Modal = _BuildDialog( '汇入模组清单', $Body, [$okButton, $CancelButton ],fnCancel );
deferred.promise( Modal );
Modal.Show();
}
}
function tryParseJson(str) {
try {
var obj = JSON.parse(str);
} catch (e) {
return {success:false};
}
return {success:true,data:obj};
}
function importing(data){
try{
if(typeof data === "string"){
var data = tryParseData(data);
}
setImportListData(data);
if(data.appid!==getAppID()){
throw new Error(`该存档字串游戏为 ${data.name}(${data.appid}) 不符合当前游戏 ${getAppName()}(${getAppID()})!`);
return;
}
ShowConfirmDialog('清空模组列表','请问是否要清空现有的模组列表?','清空','保留')
.done(
function(){
setFlag('TASK_CLEAR_IMPORT');
startExport();
})
.fail(
function(){
SubscribeAll(data.mods,getAppID());
});
}catch(e){
ShowAlertDialog('错误',e.message);
}
}
function tryParseData(text){
if(text.trim()===''){
throw new Error('字串不能为空!');
return;
}
text = text.trim();
let data = tryParseJson(text);
if(data.success){
return data.data;
}else{
try{
let data = JSON.parse(LZString.decompressFromBase64(text));
return data;
}catch (e) {
throw new Error('字串解析错误!');
return;
}
}
}
function saveListText(data){
let text = data.mods.map(item=>`${item.name} https://steamcommunity.com/sharedfiles/filedetails/?id=${item.id}`).join('\r\n');
saveText(text,`${getUserID()} ${getAppName()}(${getAppID()}) ${data.date} ${data.mods.length}个模组 清单.txt`);
}
function saveJson(data){
let text = JSON.stringify(data);
saveText(text,`${getUserID()} ${getAppName()}(${getAppID()}) ${data.date} ${data.mods.length}个模组 JSON.json`);
}
function saveText(text,filename){
let dialog = ShowBlockingWaitDialog('匯出資料','脚本正在生成檔案中,请稍后...')
setTimeout(()=>dialog.Dismiss(),3000);
let data = new Blob([text], {type: 'text/plain;charset=utf-8'});
saveAs(data, filename);
}
function timeout(time){
return new Promise(function(resolve, reject) {
setTimeout(resolve,time);
});
}
function SubscribeAll(mods, appID ){
let progressText = $NJ('<span/>');
progressText.append('正在进行订阅程序...');
let progressTextUpdated = $NJ(`<span>${mods[0].name}(0/${mods.length})</span>`);
progressText.append(progressTextUpdated);
let progress = ShowBlockingWaitDialog('处理中',progressText)
var p = Promise.resolve();
mods.forEach((mod,index) =>
p = p.then(() => timeout(100)).then(()=>SubscribeItem(mod,appID)).then(()=>progressTextUpdated.text((index+1)>=mods.length?`訂閱完成,共计${mods.length}个模组`:`${mods[index+1].name}(${index+1}/${mods.length})`))
);
p.catch(function(e){
progress.Dissmiss();
ShowAlertDialog( '錯誤', '訂閱時發生錯誤:'+e.message );
});
p.then(function(){
progress.Dismiss();
ShowAlertDialog( '成功', `訂閱完成,共计${mods.length}个模组` ).done(
function(){
location.href = URI().search(function(data) {
data.p = 1;
return data;
}).toString();
});
});
}
function SubscribeItem( mod, appID )
{
return new Promise(function(resolve, reject) {
$('PublishedFileSubscribe').id.value = mod.id;
$('PublishedFileSubscribe').appid.value = appID;
$('PublishedFileSubscribe').request( {
onSuccess: resolve
} );
});
}
function UnsubscribeAll()
{
var confirmDialog = ShowConfirmDialog( '全部取消訂閱?', '您確定要取消所有 Starbound 的訂閱嗎?<br><br>此動作無法復原!' );
return new Promise(function(resolve, reject) {
confirmDialog.done( function() {
var waitingDialog = ShowBlockingWaitDialog( '請稍候', '正在取消您的訂閱…' );
$NJ.post(
'https://steamcommunity.com/sharedfiles/unsubscribeall/',
{ 'sessionid' : getSessionID(), 'appid': getAppID(), 'filetype' : 18 }
).done( function( data ) {
waitingDialog.Dismiss();
if ( data.success == 1 )
{
waitingDialog.Dismiss();
resolve();
}
else
{
ShowAlertDialog( '錯誤', '取消訂閱時發生錯誤' );
reject(new Error('取消訂閱時發生錯誤'))
}
});
}).fail(
function(){
reject(new Error('使用者取消'));
}
);
});
}
function clearImportTask(){
clearFlag('TASK_CLEAR_IMPORT');
UnsubscribeAll()
.then(()=>SubscribeAll(getImportListData().mods,getAppID()))
.catch((e)=>ShowAlertDialog( '錯誤', e.message ));
}
if(getStatus()=="STOP"){
}else if(getStatus()=="PROCESSING"){
let processing_dialog = ShowBlockingWaitDialog('汇出MOD清单','脚本正在进行模组资料收集作业,请稍后...');
if(currentMaxItems() != '30'){
selectMaxItems('30');
return;
}
let currentList = getListData();
currentList.push(...getPageList());
setListData(currentList);
if(hasNextPage()){
goNextPage();
}else{
let date = new Date();
let data = {
appid:getAppID(),
name:getAppName(),
date:`${date.getFullYear()}-${date.getMonth()}-${date.getDay()}`,
mods:currentList
}
setStatus('STOP');
processing_dialog.Dismiss();
{
let $Body = $NJ('<div/>');
$Body.append( $NJ(`<p>本次的模组共${data.mods.length}个${getFlag('TASK_CLEAR_IMPORT')?',清空前请妥善保存模组存档资料':''}</p>`));
//$Body.append( $NJ(`<p>模組清单json</p>`));
//let $TextArea = $NJ('<textarea/>' );
//$TextArea.text( JSON.stringify(data) );
//$TextArea.click(function(){this.select();})
//$Body.append( $NJ('<div/>', {'class': 'newmodal_prompt_with_textarea gray_bevel fullwidth ' } ).append( $TextArea ) );
$Body.append( $NJ(`<p>存档字串,直接复制即可使用</p>`));
let $TextArea = $NJ('<textarea/>' , { 'class': 'newmodal_prompt_textarea', 'readonly':true } );
$TextArea.text( LZString.compressToBase64(JSON.stringify(data)));
$TextArea.click(function(){this.select();})
$Body.append( $NJ('<div/>', {'class': 'newmodal_prompt_with_textarea gray_bevel fullwidth ' } ).append( $TextArea ) );
let $copyButton = $NJ('<button/>', {type: 'submit', 'class': 'btn_green_white_innerfade btn_medium' } ).append( $NJ( '<span/>' ).text( '复制' ) );
$copyButton.click( function(){
$TextArea.select();
document.execCommand('copy');
ShowAlertDialog('成功','字串已复制到剪贴簿!');
} );
let $listButton = $NJ('<button/>', {type: 'submit', 'class': 'btn_green_white_innerfade btn_medium' } ).append( $NJ( '<span/>' ).text( getFlag('TASK_CLEAR_IMPORT')?'备份列表':'儲存列表' ) );
$listButton.click( function(){
saveListText(data);
} );
let $jsonButton = $NJ('<button/>', {type: 'submit', 'class': 'btn_green_white_innerfade btn_medium' } ).append( $NJ( '<span/>' ).text( getFlag('TASK_CLEAR_IMPORT')?'备份JSON':'儲存JSON' ) );
$jsonButton.click( function(){
saveJson(data);
} );
let buttonList = [$copyButton, $listButton, $jsonButton ];
if(getFlag('TASK_CLEAR_IMPORT')){
let $continueClearImportButton = $NJ('<button/>', {type: 'submit', 'class': 'btn_darkred_white_innerfade btn_medium' } ).append( $NJ( '<span/>' ).text( '清除所有并继续导入' ) );
$continueClearImportButton.click( function(){
clearImportTask();
Modal.Dismiss();
} );
buttonList.push($continueClearImportButton)
}
let Modal = _BuildDialog( getFlag('TASK_CLEAR_IMPORT')?'模组列表备份完成':'模组列表收集完成', $Body,buttonList,function(){ Modal.Dismiss();;} );
Modal.Show();
clearFlag('TASK_CLEAR_IMPORT');
}
}
}else{
console.error('strange status'+ getStatus());
}
})();