First ui version for pip
This commit is contained in:
1 parent
361e4adcb4
commit
a1bc8200de
15 files changed
+312
-77
No files matched your search
@@ -9,6 +9,7 @@ old/
|
||||
venv./
|
||||
debug.py
|
||||
config.yaml
|
||||
!biliup/public/config.yaml
|
||||
chromedriver.*
|
||||
/configlog.ini
|
||||
ds_update.log*
|
||||
@@ -25,3 +26,5 @@ build/
|
||||
bili.cookie.json
|
||||
archive.txt
|
||||
/node_modules
|
||||
watch_process.pid
|
||||
package-lock.json
|
||||
+18
-11
@@ -3,6 +3,7 @@ from http import cookies
|
||||
from http.client import OK
|
||||
import json
|
||||
from multiprocessing.sharedctypes import Value
|
||||
import os
|
||||
|
||||
from aiohttp import streamer
|
||||
|
||||
@@ -65,31 +66,37 @@ async def main(args):
|
||||
return web.json_response(config.data['streamers'])
|
||||
async def set_streamer_config(request):
|
||||
post_data =await request.json()
|
||||
for i,j in post_data['streamers'].items():
|
||||
if i not in config.data['streamers']:
|
||||
config.data['streamers'][i]={}
|
||||
for key,Value in j.items():
|
||||
config.data['streamers'][i][key]=Value
|
||||
for i in config.data['streamers']:
|
||||
if i not in post_data['streamers']:
|
||||
del config.data['streamers'][i]
|
||||
config.data['streamers']=post_data['streamers']
|
||||
# for i,j in post_data['streamers'].items():
|
||||
# if i not in config.data['streamers']:
|
||||
# config.data['streamers'][i]={}
|
||||
# for key,Value in j.items():
|
||||
# config.data['streamers'][i][key]=Value
|
||||
# for i in config.data['streamers']:
|
||||
# if i not in post_data['streamers']:
|
||||
# del config.data['streamers'][i]
|
||||
|
||||
print("sucess")
|
||||
return web.json_response({"status":200},status=200)
|
||||
async def save_config(reequest):
|
||||
config.save()
|
||||
return web.json_response({"status":200},status=200)
|
||||
async def root_handler(request):
|
||||
return web.HTTPFound('/index.html')
|
||||
app = web.Application()
|
||||
|
||||
|
||||
web_dir = os.path.dirname(__file__)
|
||||
public_dir=(os.path.join(web_dir, 'public'))
|
||||
build_dir =(os.path.join(public_dir, 'build'))
|
||||
app.add_routes([web.get('/url-status', url_status)])
|
||||
app.add_routes([web.get('/api/basic',get_basic_config)])
|
||||
app.add_routes([web.post('/api/setbasic',set_basic_config)])
|
||||
app.add_routes([web.get('/api/getconfig',get_streamer_config)])
|
||||
app.add_routes([web.post('/api/setconfig',set_streamer_config)])
|
||||
app.add_routes([web.get('/api/save',save_config)])
|
||||
app.add_routes([web.static('/public','./public',show_index=False)])
|
||||
app.add_routes([web.static('/build','./public/build',show_index=False)])
|
||||
app.add_routes([web.get('/', root_handler)])
|
||||
app.add_routes([web.static('/',public_dir,show_index=False)])
|
||||
app.add_routes([web.static('/build',build_dir,show_index=False)])
|
||||
cors = aiohttp_cors.setup(app, defaults={
|
||||
"*": aiohttp_cors.ResourceOptions(
|
||||
allow_credentials=True,
|
||||
|
||||
+4
-1
@@ -72,7 +72,10 @@ def arg_parser():
|
||||
parser_restart.set_defaults(func=daemon.restart)
|
||||
parser.set_defaults(func=lambda: asyncio.run(main(args)))
|
||||
args = parser.parse_args()
|
||||
config.load(args.config)
|
||||
if(args.http):
|
||||
config.create_without_config_input(args.config)
|
||||
else:
|
||||
config.load(args.config)
|
||||
LOG_CONF.update(config.get('LOGGING', {}))
|
||||
if args.verbose:
|
||||
LOG_CONF['loggers']['biliup']['level'] = args.verbose
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from collections import UserDict
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import biliup
|
||||
from .decorators import Plugin
|
||||
|
||||
|
||||
@@ -9,6 +13,18 @@ class Config(UserDict):
|
||||
file = open('config.yaml', encoding='utf-8')
|
||||
with file as stream:
|
||||
self.data = yaml.load(stream, Loader=yaml.FullLoader)
|
||||
def create_without_config_input(self, file):
|
||||
import yaml
|
||||
if file is None:
|
||||
try:
|
||||
file = open('config.yaml', encoding='utf-8')
|
||||
except FileNotFoundError:
|
||||
biliup_dir = os.path.dirname(biliup.__file__)
|
||||
config_data = os.path.join(biliup_dir,"public/","config.yaml")
|
||||
shutil.copy(config_data, os.getcwd())
|
||||
file = open('config.yaml', encoding='utf-8')
|
||||
with file as stream:
|
||||
self.data = yaml.load(stream, Loader=yaml.FullLoader)
|
||||
def save(self):
|
||||
import yaml
|
||||
old_file = open('config.yaml',encoding='utf-8')
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
user: # 在填了cookies的情况下优先使用cookies上传,如需使用用户名密码上传请注释掉cookies
|
||||
cookies:
|
||||
SESSDATA: your SESSDATA
|
||||
bili_jct: your bili_jct
|
||||
DedeUserID__ckMd5: your ckMd5
|
||||
DedeUserID: your DedeUserID
|
||||
access_token: your access_key
|
||||
# account:
|
||||
# username: your usrname
|
||||
# password: your password
|
||||
# app_key: bca7e84c2d947ac6 # 若账号密码方式无法登录可尝试更改此值
|
||||
# appsec: 60698ba2f68e01ce44738920a0ffe768
|
||||
|
||||
# 选择全局默认上传插件,Noop为不上传,但会执行后处理
|
||||
#uploader: Noop
|
||||
|
||||
# b站上传线路选择,默认为自动模式,目前可手动切换为bda2, kodo, ws, qn, cos, cos-internal(支持腾讯云内网免流+提速)
|
||||
lines: AUTO
|
||||
# b站提交接口,默认自动选择,可选web,client
|
||||
submit_api: client
|
||||
# 单文件并发上传数,未达到带宽上限时增大此值可提高上传速度
|
||||
threads: 3
|
||||
# 录像单文件大小限制,单位Byte,超过此大小分段下载
|
||||
file_size: 2621440000
|
||||
# 录像单文件时间限制,格式'00:00:00'(时分秒),超过此大小分段下载,如需使用大小分段请注释此字段
|
||||
#segment_time: '00:50:00'
|
||||
filtering_threshold: 20 # 小于此大小的视频文件将会被过滤删除,单位MB
|
||||
# 如遇到斗鱼录制卡顿可以尝试切换线路,tct-h5(备用线路5),ali-h5(备用线路6),akm-h5(主线路1)
|
||||
#douyucdn: tct-h5
|
||||
# 如遇到虎牙录制卡顿可以尝试切换线路,AL(阿里),BD(百度),TX(腾讯)
|
||||
#huyacdn: AL
|
||||
# 如遇到哔哩哔哩录制跳帧可以尝试修改platform值,web(flv),h5(m3u8)
|
||||
#biliplatform: web
|
||||
# 如需要录制抖音请在此填入cookie需要__ac_nonce与__ac_signature的值
|
||||
#douyin_cookie: '__ac_nonce=123456; __ac_signature=123456;'
|
||||
# Netscape 格式的 Cookies 文本路径
|
||||
#youtube_cookie: 'cookiejar.txt'
|
||||
|
||||
streamers:
|
||||
星际2Stats拔本神族天梯第一视角: # 最小配置示例
|
||||
url:
|
||||
- https://www.twitch.tv/kimdaeyeob3
|
||||
星际2INnoVation吕布卫星人族天梯第一视角: # 完整可选配置示例
|
||||
url:
|
||||
- https://www.twitch.tv/innovation_s2
|
||||
- https://www.panda.tv/1160340
|
||||
title: "{title}第一视角%Y-%m-%d" # 自定义标题的时间格式, {title}代表直播间标题
|
||||
tid: 171 # 投稿分区码,171为电子竞技分区
|
||||
copyright: 2 # 1为自制
|
||||
cover_path: /cover/up.jpg
|
||||
description: "视频简介: {title} %Y-%m-%d %H:%M:%S" # 支持strftime, {title}占位符
|
||||
dynamic: '#空间动态#'
|
||||
dtime: 14400 # 设置延时发布时间,距离提交大于2小时,格式为时间戳
|
||||
# 覆盖全局默认上传插件,Noop为不上传,但会执行后处理
|
||||
uploader: bili_web
|
||||
postprocessor: # 上传完成后,将按自定义顺序执行自定义操作
|
||||
- run: echo hello! # 执行任意命令,等同于在shell中运行,视频文件路径作为标准输入传入
|
||||
- mv: backup/ # 移动文件到backup目录下
|
||||
# - rm # 删除文件,为默认操作
|
||||
tags:
|
||||
- 星际争霸2
|
||||
- 电子竞技
|
||||
format: mp4 # 视频保存格式
|
||||
opt_args: # ffmpeg参数
|
||||
- '-ss' # 跳过开始的16秒
|
||||
- '00:00:16'
|
||||
|
||||
# 检测间隔时间,单位:秒
|
||||
event_loop_interval: 40
|
||||
# 相同平台检测间隔,单位:秒。不同平台的链接是并发的,不受此参数影响
|
||||
checker_sleep: 15
|
||||
# 线程池1大小,负责download事件
|
||||
pool1_size: 3
|
||||
# 线程池2大小,处理除download事件外所有其他事件
|
||||
pool2_size: 3
|
||||
# 检测源码文件变化间隔,单位:秒,检测源码到变化后,程序会在空闲时自动重启
|
||||
check_sourcecode: 15
|
||||
# 日志输出配置
|
||||
LOGGING:
|
||||
formatters:
|
||||
verbose:
|
||||
format: '%(asctime)s %(filename)s[line:%(lineno)d](Pid:%(process)d Tname:%(threadName)s) %(levelname)s %(message)s'
|
||||
datefmt: '%Y-%m-%d %H:%M:%S'
|
||||
simple:
|
||||
format: '%(filename)s%(lineno)d[%(levelname)s]Tname:%(threadName)s %(message)s'
|
||||
handlers:
|
||||
console:
|
||||
level: DEBUG
|
||||
class: logging.StreamHandler
|
||||
formatter: simple
|
||||
stream: ext://sys.stdout
|
||||
file:
|
||||
level: DEBUG
|
||||
class: biliup.common.log.SafeRotatingFileHandler
|
||||
when: W0
|
||||
interval: 1
|
||||
backupCount: 1
|
||||
filename: ds_update.log
|
||||
formatter: verbose
|
||||
root:
|
||||
handlers: [ console ]
|
||||
level: INFO
|
||||
loggers:
|
||||
biliup:
|
||||
handlers: [ file ]
|
||||
level: INFO
|
||||
# 默认通过网页接口上传,可选通过操作chrome上传,此时需要填写chromedriver路径
|
||||
#chromedriver_path: /usr/local/bin/chromedriver
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,31 +0,0 @@
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 0.5rem;
|
||||
/*height: 5px;*/
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
border-radius: 100vh;
|
||||
background: #ffffff00;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e0;
|
||||
border-radius: 100vh;
|
||||
border: 1px solid #edf2f7;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #a0aec0;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.tippy-content{
|
||||
padding: 0 !important;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
|
||||
<title>Svelte app</title>
|
||||
|
||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
<link rel='stylesheet' href='/global.css'>
|
||||
<link rel='stylesheet' href='/build/bundle.css'>
|
||||
<script defer src='/build/bundle.js'></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -8,3 +8,4 @@ PyYAML>=4.2b1
|
||||
streamlink>=0.14.2
|
||||
ykdl>=1.8.0
|
||||
rsa>=4.6
|
||||
aiohttp_cors
|
||||
+1
-1
@@ -37,7 +37,7 @@ export default {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/build/bundle.js'
|
||||
file: 'biliup/public/build/bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
|
||||
@@ -25,7 +25,7 @@ install_requires =
|
||||
rsa >= 4.6
|
||||
|
||||
[options.package_data]
|
||||
biliup = common/*.ini,public/*
|
||||
biliup = common/*.ini,public/*,public/build/*
|
||||
|
||||
[options.packages.find]
|
||||
include = biliup*
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
import setuptools
|
||||
setuptools.setup()
|
||||
setuptools.setup(
|
||||
package_dir={'build':'biliup/'}
|
||||
)
|
||||
+46
-11
@@ -63,6 +63,7 @@
|
||||
|
||||
// console.log()
|
||||
let tags = selectedTemplate?.tags ? selectedTemplate?.tags : [];
|
||||
let urls = selectedTemplate?.url ? selectedTemplate?.url : [];
|
||||
// $: tags = selectedTemplate?.tag.split(',');
|
||||
|
||||
let parent = '请选择';
|
||||
@@ -98,7 +99,7 @@
|
||||
}
|
||||
|
||||
let tempTag;
|
||||
|
||||
let tempUrl;
|
||||
|
||||
|
||||
function handleKeypress() {
|
||||
@@ -109,16 +110,32 @@
|
||||
return;
|
||||
}
|
||||
tags = [...tags, tempTag];
|
||||
selectedTemplate.tag = tags.join(',');
|
||||
selectedTemplate.tags = tags;
|
||||
tempTag = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
function urlhandleKeypress(){
|
||||
console.log(urls);
|
||||
if(urls.includes(tempUrl)){
|
||||
createPop("已有相同链接");
|
||||
tempUrl = null;
|
||||
return;
|
||||
}
|
||||
urls = [...urls, tempUrl];
|
||||
selectedTemplate.url = urls;
|
||||
tempUrl=null;
|
||||
return false;
|
||||
}
|
||||
function removeTag(tag) {
|
||||
tags = tags.filter(t => t !== tag);
|
||||
selectedTemplate.tag = tags.join(',');
|
||||
selectedTemplate.tags = tags;
|
||||
console.log(tag);
|
||||
}
|
||||
function removeUrl(url) {
|
||||
urls = urls.filter(u => u !== url);
|
||||
selectedTemplate.url = urls;
|
||||
console.log(url);
|
||||
}
|
||||
|
||||
|
||||
function callback(detailTid, detailParent, detailChildren) {
|
||||
@@ -181,13 +198,31 @@
|
||||
placeholder="标题">
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label class="label">
|
||||
<span class="text-sm font-bold text-gray-500 tracking-wide">直播间链接</span>
|
||||
</label>
|
||||
<input bind:value={selectedTemplate.url}
|
||||
class="text-base p-2 border border-gray-300 rounded-lg focus:outline-none focus:border-indigo-500"
|
||||
placeholder="www.douyu.com/3484">
|
||||
<!-- <div class="flex flex-col">-->
|
||||
<!-- <label class="label">-->
|
||||
<!-- <span class="text-sm font-bold text-gray-500 tracking-wide">直播间链接</span>-->
|
||||
<!-- </label>-->
|
||||
<!-- <input bind:value={selectedTemplate.url}-->
|
||||
<!-- class="text-base p-2 border border-gray-300 rounded-lg focus:outline-none focus:border-indigo-500"-->
|
||||
<!-- placeholder="www.douyu.com/3484">-->
|
||||
<!-- </div>-->
|
||||
<div class="flex flex-wrap rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent">
|
||||
{#each urls as url(url)}
|
||||
<span animate:flip="{{duration: 300}}" class="flex ml-1 my-1.5 px-3 py-0.5 text-base rounded-full text-white bg-indigo-500 ">
|
||||
{url}
|
||||
<button on:click={(e)=>{removeUrl(url)}} class="bg-transparent hover">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor"
|
||||
class="ml-2" viewBox="0 0 1792 1792">
|
||||
<path d="M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
{/each}
|
||||
|
||||
<input bind:value={tempUrl} class="outline-none rounded-lg flex-1 appearance-none w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base " on:keypress={e=>e.key==='Enter' && urlhandleKeypress()}
|
||||
placeholder="录制链接"
|
||||
type="text"/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
+111
-3
@@ -1,5 +1,9 @@
|
||||
import { writable } from "svelte/store";
|
||||
import { crossfade, fly } from "svelte/transition";
|
||||
import {writable} from "svelte/store";
|
||||
import {open} from "@tauri-apps/api/dialog";
|
||||
import {sep} from "@tauri-apps/api/path";
|
||||
import {invoke} from "@tauri-apps/api/tauri";
|
||||
import {crossfade, fly} from "svelte/transition";
|
||||
import {createPop} from "./common";
|
||||
|
||||
|
||||
export const isLogin = writable(true);
|
||||
@@ -24,7 +28,111 @@ export const currentTemplate = writable({
|
||||
export const [send, receive] = crossfade({
|
||||
duration: 800,
|
||||
fallback: (node, params) => {
|
||||
return fly(node, { x: 200, delay: 500 });
|
||||
return fly(node, {x: 200, delay: 500});
|
||||
},
|
||||
});
|
||||
export const fileselect = () => {
|
||||
let properties = {
|
||||
// defaultPath: 'C:\\',
|
||||
multiple: true,
|
||||
// directory: false,
|
||||
filters: [{
|
||||
extensions: ['mp4', 'flv', 'avi', 'wmv', 'mov', 'webm', 'mpeg4', 'ts', 'mpg', 'rm', 'rmvb', 'mkv', 'm4v'],
|
||||
name: ""
|
||||
}]
|
||||
};
|
||||
open(properties).then((pathStr) => {
|
||||
console.log(pathStr);
|
||||
if (!pathStr) return;
|
||||
attach(pathStr);
|
||||
});
|
||||
};
|
||||
|
||||
export function attach(files) {
|
||||
currentTemplate.update(temp => {
|
||||
function findFile(file) {
|
||||
return temp.selectedTemplate['files'].find((existingFile) => existingFile.id === file);
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (findFile(file)) {
|
||||
createPop('请上传非重复视频!');
|
||||
continue;
|
||||
}
|
||||
let split = file.split(sep);
|
||||
let filename = split[split.length - 1];
|
||||
|
||||
// temp['files'] = [...temp['files'], ...event.target.files];
|
||||
temp.selectedTemplate['files'].push({
|
||||
filename: file,
|
||||
id: file,
|
||||
title: filename.substring(0, filename.lastIndexOf(".")),
|
||||
desc: '',
|
||||
progress: 0,
|
||||
speed: 0,
|
||||
totalSize: 0,
|
||||
complete: false,
|
||||
process: false,
|
||||
});
|
||||
// let objectURL = URL.createObjectURL(file);
|
||||
// console.log(objectURL);
|
||||
}
|
||||
const res = allComplete(temp.selectedTemplate['files'], temp.selectedTemplate);
|
||||
console.log(res);
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
function allComplete(files, temp) {
|
||||
// console.log(temp);
|
||||
for (const file of files) {
|
||||
if (!file.complete && !file.process && temp.atomicInt < 1) {
|
||||
temp.atomicInt++;
|
||||
file.process = true;
|
||||
upload(file, temp);
|
||||
console.log(temp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function upload(video, temp) {
|
||||
// const files = [];
|
||||
|
||||
invoke('upload', {
|
||||
video: video
|
||||
}).then((res) => {
|
||||
// temp.atomicInt--;
|
||||
video.filename = res[0].filename;
|
||||
video.speed = res[1];
|
||||
video.complete = true;
|
||||
video.progress = 100;
|
||||
currentTemplate.update(t => {
|
||||
t.selectedTemplate.files.forEach(file => {
|
||||
if (file.id === video.id) {
|
||||
file.filename = res[0].filename;
|
||||
file.speed = res[1];
|
||||
file.complete = true;
|
||||
file.progress = 100;
|
||||
}
|
||||
})
|
||||
return t;
|
||||
});
|
||||
console.log(`Message:`, res);
|
||||
}).catch((e) => {
|
||||
createPop(`${video.filename}: ${e}`, 5000);
|
||||
console.log(e);
|
||||
}).finally(() => {
|
||||
temp.atomicInt--;
|
||||
if (allComplete(temp['files'], temp)) {
|
||||
console.log(temp.submitCallback);
|
||||
if (temp.submitCallback) {
|
||||
temp.submitCallback();
|
||||
temp.submitCallback=null;
|
||||
}
|
||||
console.log("allComplete");
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user