mirror of https://gitgud.io/fatchan/jschan.git
# Conflicts: # controllers/forms/globalsettings.js # gulpfile.js # migrations/0.8.0.js # package.jsonindiachan-spamvector
commit
5c359a8bda
23 changed files with 381 additions and 34 deletions
@ -0,0 +1,67 @@ |
||||
//https://github.com/ussaohelcim/ptchina-playlist/tree/bookmarklet-let
|
||||
async function threadToPlaylist(board, postId) { |
||||
async function getThread() { |
||||
let link = `${window.location.origin}/${board}/thread/${postId}.json`; |
||||
return await fetch(link).then(res => res.json()); |
||||
} |
||||
function isAudioOrVideo(file) { |
||||
const mimeTypes = ['video', 'audio']; |
||||
const fileType = file.mimetype.split('/')[0]; |
||||
return fileType === mimeTypes[0] || fileType === mimeTypes[1]; |
||||
} |
||||
async function getMedia(thread) { |
||||
const files = []; |
||||
thread.files.forEach((file) => { |
||||
if (isAudioOrVideo(file)) { |
||||
files.push(file); |
||||
} |
||||
}); |
||||
for (let i = 0; i < thread.replies.length; i++) { |
||||
const replyFiles = thread.replies[i].files; |
||||
replyFiles.forEach((file) => { |
||||
if (isAudioOrVideo(file)) { |
||||
files.push(file); |
||||
} |
||||
}); |
||||
} |
||||
return files; |
||||
} |
||||
function createPlaylist(medias) { |
||||
const lines = []; |
||||
lines.push('#EXTM3U'); |
||||
for (let i = 0; i < medias.length; i++) { |
||||
const media = medias[i]; |
||||
lines.push(`#EXTINF:${media.duration}, ${media.originalFilename}`); |
||||
lines.push(`${location.origin}/file/${media.filename}`); |
||||
} |
||||
let playlist = lines.join('\n'); |
||||
return playlist; |
||||
} |
||||
function downloadPlaylist(filename, playlist) { |
||||
const blob = new Blob([playlist], { type: 'application/mpegurl' }); |
||||
const url = window.URL.createObjectURL(blob); |
||||
const a = document.createElement('a'); |
||||
a.href = url; |
||||
a.target = '_blank'; |
||||
a.download = filename; |
||||
a.style.display = 'none'; |
||||
document.body.appendChild(a); |
||||
a.click(); |
||||
document.body.removeChild(a); |
||||
} |
||||
try { |
||||
const thread = await getThread(); |
||||
const files = await getMedia(thread); |
||||
const playlist = await createPlaylist(files); |
||||
if (playlist.split('\n').length > 1) { |
||||
downloadPlaylist(`${thread.board}-${thread.postId}.m3u`, playlist); |
||||
} else { |
||||
console.log('No video/audio files in this thread.'); |
||||
} |
||||
} catch (error) { |
||||
console.log(error); |
||||
} |
||||
} |
||||
window.addEventListener('createPlaylist', (e) => { |
||||
threadToPlaylist(e.detail.board, e.detail.postId); |
||||
}); |
@ -0,0 +1,44 @@ |
||||
'use strict'; |
||||
|
||||
const { debugLogs } = require(__dirname+'/../../configs/secrets.js') |
||||
, config = require(__dirname+'/../../lib/misc/config.js') |
||||
, { Boards } = require(__dirname+'/../../db/') |
||||
, timeUtils = require(__dirname+'/../../lib/converter/timeutils.js') |
||||
, deleteBoard = require(__dirname+'/../../models/forms/deleteboard.js'); |
||||
|
||||
module.exports = { |
||||
|
||||
func: async () => { |
||||
|
||||
if (config.get.abandonedBoardAction === 0) { |
||||
return; |
||||
} |
||||
|
||||
const abandonedBoards = await Boards.getAbandoned(config.get.abandonedBoardAction); |
||||
if (abandonedBoards.length === 0) { |
||||
return; |
||||
} |
||||
|
||||
if (config.get.abandonedBoardAction <= 2) { |
||||
debugLogs && console.log(`Locking${config.get.abandonedBoardAction === 2 ? '+Unlisting' : ''} ${abandonedBoards.length} abandoned boards.`); |
||||
const abandonedURIs = abandonedBoards.map(b => b._id); |
||||
Boards.unlistMany(abandonedURIs); |
||||
} else { //must be 2
|
||||
//run delete board model for each
|
||||
debugLogs && console.log(`Deleting ${abandonedBoards.length} abandoned boards.`); |
||||
for (let board of abandonedBoards) { |
||||
try { |
||||
await deleteBoard(board._id, board); |
||||
} catch (err) { |
||||
debugLogs && console.log('Error deleting abandoned board:', err); |
||||
} |
||||
} |
||||
} |
||||
|
||||
}, |
||||
|
||||
interval: timeUtils.DAY, |
||||
immediate: false, |
||||
condition: 'abandonedBoardAction' |
||||
|
||||
}; |
@ -0,0 +1,98 @@ |
||||
'use strict'; |
||||
|
||||
const { debugLogs } = require(__dirname+'/../../configs/secrets.js') |
||||
, config = require(__dirname+'/../../lib/misc/config.js') |
||||
, Redis = require(__dirname+'/../../lib/redis/redis.js') |
||||
, { Boards, Accounts } = require(__dirname+'/../../db/') |
||||
, timeUtils = require(__dirname+'/../../lib/converter/timeutils.js'); |
||||
|
||||
module.exports = { |
||||
|
||||
func: async () => { |
||||
|
||||
if (config.get.inactiveAccountAction === 0) { |
||||
return; |
||||
} |
||||
|
||||
const inactiveAccounts = await Accounts.getInactive(config.get.inactiveAccountTime); |
||||
if (inactiveAccounts.length === 0) { |
||||
return; |
||||
} |
||||
|
||||
const cacheDeleteSet = new Set() |
||||
, boardBulkWrites = [] |
||||
, inactiveWithBoards = inactiveAccounts.filter(acc => { |
||||
//only deal with boards if they have any (acc deletes still processed later)
|
||||
return acc.ownedBoards.length > 0 || acc.staffBoards.length > 0; |
||||
}); |
||||
|
||||
let boardsPromise = null |
||||
, accountsPromise = null; |
||||
|
||||
//create promise for boards (remove staff.${username})
|
||||
inactiveWithBoards.forEach(acc => { |
||||
//remove account from staff and owner of all their boards
|
||||
const accountBoards = [...acc.ownedBoards, ...acc.staffBoards]; |
||||
accountBoards.forEach(b => cacheDeleteSet.add(b)); |
||||
boardBulkWrites.push({ |
||||
updateOne: { |
||||
filter: { |
||||
'_id': { |
||||
'$in': accountBoards, |
||||
//better to do per board for staff unsets? or per-account...
|
||||
} |
||||
}, |
||||
update: { |
||||
$unset: { |
||||
[`staff.${acc._id}`]: '', |
||||
}, |
||||
} |
||||
} |
||||
}); |
||||
acc.ownedBoards.forEach(ob => { |
||||
boardBulkWrites.push({ |
||||
updateOne: { |
||||
filter: { |
||||
'_id': ob, |
||||
}, |
||||
update: { |
||||
'$set': { |
||||
'owner': null, |
||||
}, |
||||
} |
||||
} |
||||
}); |
||||
}); |
||||
}); |
||||
if (boardBulkWrites.length > 0) { |
||||
boardsPromise = Boards.db.bulkWrite(boardBulkWrites); |
||||
} |
||||
|
||||
//create promise for accounts (clearing staff positions or deleting fuly)
|
||||
if (config.get.inactiveAccountAction === 2) { |
||||
debugLogs && console.log(`Deleting ${inactiveAccounts.length} inactive accounts`); |
||||
const inactiveUsernames = inactiveAccounts.map(acc => acc._id); |
||||
accountsPromise = Accounts.deleteMany(inactiveUsernames); |
||||
} else{ |
||||
debugLogs && console.log(`Removing staff positions from ${inactiveWithBoards.length} inactive accounts`); |
||||
const inactiveUsernames = inactiveWithBoards.map(acc => acc._id); |
||||
accountsPromise = Accounts.clearStaffAndOwnedBoards(inactiveUsernames); |
||||
} |
||||
|
||||
//execute promises
|
||||
await Promise.all([ |
||||
accountsPromise, |
||||
boardsPromise, |
||||
]); |
||||
|
||||
//clear caches
|
||||
cacheDeleteSet.forEach(b => Redis.del(`board:${b}`)); |
||||
//users: cache already handled by Accounts.deleteMany or Accounts.clearStaffAndOwnedBoards
|
||||
|
||||
}, |
||||
|
||||
interval: timeUtils.DAY, |
||||
immediate: false, |
||||
condition: 'inactiveAccountAction' |
||||
|
||||
}; |
Loading…
Reference in new issue