diff --git a/.gitignore b/.gitignore index 879546b..7088d36 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/biliup/__init__.py b/biliup/__init__.py index 9801c03..68f9e3e 100644 --- a/biliup/__init__.py +++ b/biliup/__init__.py @@ -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, diff --git a/biliup/__main__.py b/biliup/__main__.py index 094d55e..65de44b 100644 --- a/biliup/__main__.py +++ b/biliup/__main__.py @@ -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 diff --git a/biliup/engine/__init__.py b/biliup/engine/__init__.py index 95c9fa9..8516743 100644 --- a/biliup/engine/__init__.py +++ b/biliup/engine/__init__.py @@ -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') diff --git a/biliup/public/config.yaml b/biliup/public/config.yaml new file mode 100644 index 0000000..7d8a696 --- /dev/null +++ b/biliup/public/config.yaml @@ -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 diff --git a/public/favicon.png b/public/favicon.png deleted file mode 100644 index 7e6f5eb..0000000 Binary files a/public/favicon.png and /dev/null differ diff --git a/public/global.css b/public/global.css deleted file mode 100644 index ce7905a..0000000 --- a/public/global.css +++ /dev/null @@ -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; -} diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 0d032c1..0000000 --- a/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - Svelte app - - - - - - - - - - diff --git a/public/noface.jpg b/public/noface.jpg deleted file mode 100644 index 1663902..0000000 Binary files a/public/noface.jpg and /dev/null differ diff --git a/requirements.txt b/requirements.txt index 04b225a..dceec7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ PyYAML>=4.2b1 streamlink>=0.14.2 ykdl>=1.8.0 rsa>=4.6 +aiohttp_cors diff --git a/rollup.config.js b/rollup.config.js index 7fad607..babb272 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -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({ diff --git a/setup.cfg b/setup.cfg index 8a12a88..ae56fba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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* diff --git a/setup.py b/setup.py index 864b617..0d2d87e 100644 --- a/setup.py +++ b/setup.py @@ -1,2 +1,4 @@ import setuptools -setuptools.setup() \ No newline at end of file +setuptools.setup( + package_dir={'build':'biliup/'} +) \ No newline at end of file diff --git a/src/Upload.svelte b/src/Upload.svelte index b92b22c..e5a7aeb 100644 --- a/src/Upload.svelte +++ b/src/Upload.svelte @@ -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="标题"> -
- - + + + + + + + + +
+ {#each urls as url(url)} + + {url} + + + {/each} + + e.key==='Enter' && urlhandleKeypress()} + placeholder="录制链接" + type="text"/>
diff --git a/src/store.ts b/src/store.ts index a119295..ba3dd87 100644 --- a/src/store.ts +++ b/src/store.ts @@ -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; + } + }) +}