diff --git a/.github/workflows/stt.yml b/.github/workflows/stt.yml new file mode 100644 index 00000000..31d7f0fd --- /dev/null +++ b/.github/workflows/stt.yml @@ -0,0 +1,189 @@ +name: stt +on: + workflow_dispatch: + inputs: + bvid: + description: 'Bilibili bvid' + required: false + category: + description: 'category' + type: choice + options: + - btnews + - commercial + - opinion + required: false + schedule: + - cron: '0 22 * * *' +jobs: +# fetch audio by input & auto schedule + prepare-video-meta: + runs-on: ubuntu-latest + outputs: + exist: ${{ steps.get-video.outputs.exist }} + filepath: ${{ steps.get-video.outputs.filepath }} + index: ${{ steps.get-video.outputs.index }} + date: ${{ steps.get-video.outputs.date }} + bvid: ${{ steps.get-video.outputs.bvid }} + title: ${{ steps.get-video.outputs.title }} + description: ${{ steps.get-video.outputs.tidescriptiontle }} + category: ${{ steps.get-video.outputs.category }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: install deps + run: npm install + - name: fetch user audio video + id: get-video + run: npm run get-video -- --category ${{ github.event.inputs.category }} --bv ${{ github.event.inputs.bvid }} + fetch-audio: + runs-on: ubuntu-latest + needs: [prepare-video-meta] + if: needs.prepare-video-meta.outputs.exist == false + env: + url: https://www.bilibili.com/video/${{needs.prepare-video-meta.outputs.bvid }} + title: ${{ needs.prepare-video-meta.outputs.title }} + category: ${{ needs.prepare-video-meta.outputs.category }} + steps: + - name: install lux & ffmpeg + run: | + wget https://github.com/iawia002/lux/releases/download/v0.24.1/lux_0.24.1_Linux_arm64.tar.gz + wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz + ls -l /usr/local/bin + ls -l + tar -xvzf lux_0.24.1_Linux_arm64.tar.gz -C /usr/local/bin + mkdir -p /usr/local/ffmpeg + tar -xvJf ffmpeg-release-arm64-static.tar.xz -C /usr/local/ + mv /usr/local/ffmpeg-7.0.2-arm64-static/* /usr/local/ffmpeg + echo "hello" + ls -l /usr/local/ffmpeg + export PATH=/usr/local/bin:$PATH + export PATH=/usr/local/ffmpeg:$PATH + chmod +x /usr/local/ffmpeg/ffmpeg + ls -l /usr/local/ffmpeg + mkdir -p test + - name: download audio + run: | + export PATH=/usr/local/bin:$PATH + export PATH=/usr/local/ffmpeg:$PATH + echo $PATH + ffmpeg -version + lux -O=output -o=test ${{env.url}} + ls -l + - name: convert to mp3 + run: | + export PATH=/usr/local/bin:$PATH + export PATH=/usr/local/ffmpeg:$PATH + echo $PATH + ls -l + ls -l test + ffmpeg -i test/output.mp4 test/output.mp3 + - name: Upload output.mp3 + uses: actions/upload-artifact@v4 + with: + name: output.mp3 + path: test/output.mp3 + if-no-files-found: error + retention-days: 1 + stt: + runs-on: ubuntu-latest + if: needs.prepare-video-meta.outputs.exist == false + needs: + - fetch-audio + steps: + - name: Download output.mp3 + uses: actions/download-artifact@v4 + with: + name: output.mp3 + path: /tmp + - name: upload to dify + id: difyfile + run: | + curl -X POST 'https://api.dify.ai/v1/files/upload' \ + --header 'Authorization: Bearer ${{secrets.DIFY_TOKEN}}' \ + --form 'file=@/tmp/output.mp3;type=audio/mp3' \ + --form 'user=${{secrets.DIFY_USER}}' | jq -r '.id' | xargs -I {} echo "DIFY_AUDIO_FILE_ID={}" >> "$GITHUB_OUTPUT" + - name: call dify + run: | + echo $DIFY_AUDIO_FILE_ID + curl --max-time 600 -X POST 'https://api.dify.ai/v1/workflows/run' \ + --header 'Authorization: Bearer ${{secrets.DIFY_TOKEN}}' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "inputs": { + "audio": { + "transfer_method": "local_file", + "upload_file_id": "${{ steps.difyfile.outputs.DIFY_AUDIO_FILE_ID }}", + "type": "audio" + } + }, + "response_mode": "blocking", + "user": "${{secrets.DIFY_USER}}" + }' | jq -r '.data.outputs.text' > output.md + - name: show result + run: | + cat output.md + - name: Upload output.md + uses: actions/upload-artifact@v4 + with: + name: output.md + path: output.md + if-no-files-found: error + retention-days: 1 + create-pr: + if: needs.prepare-video-meta.outputs.exist == false + runs-on: ubuntu-latest + needs: [stt, prepare-video-meta] + steps: + - name: mkdir + run: mkdir -p /tmp1 + - name: Download output.md + uses: actions/download-artifact@v4 + with: + name: output.md + path: /tmp1 + - name: createHead + run: | + echo '--- + title: ${{needs.prepare-video-meta.outputs.title }} + description: ${{needs.prepare-video-meta.outputs.description }} + tag: [] + date: ${{needs.prepare-video-meta.outputs.date }} + bvid: ${{needs.prepare-video-meta.outputs.bvid }} + --- + ' >> tmp.md + ls -l /tmp1 + cat /tmp1/output.md >> tmp.md + mv tmp.md ${{ needs.prepare-video-meta.outputs.filepath }} + - name: Upload res.md + uses: actions/upload-artifact@v4 + with: + name: tmp.md + path: tmp.md + if-no-files-found: error + retention-days: 1 + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.PAT }} + commit-message: ":memo: new ${{needs.prepare-video-meta.outputs.category}} ${{ needs.prepare-video-meta.outputs.title }}" + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + delete-branch: true + branch: fetch/${{ needs.prepare-video-meta.outputs.index }} + title: ':memo: new ${{needs.prepare-video-meta.outputs.category}} ${{ needs.prepare-video-meta.outputs.title }}' + add-paths: | + docs/**/*.md + images/**/*.webp + base: master + body: | + new ${{needs.prepare-video-meta.outputs.category}} ${{ needs.prepare-video-meta.outputs.title }} + - Auto-generated by action: [create-pull-request](https://github.com/peter-evans/create-pull-request) with dify + labels: | + ${{needs.prepare-video-meta.outputs.category}} + automated pr + draft: false \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 69c58154..2ee1ba2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "name": "btnews", "version": "0.0.1", "license": "MIT", - "dependencies": { - "ts-node": "^10.9.2" - }, "devDependencies": { "@actions/core": "^1.10.1", "@types/cheerio": "^0.22.35", @@ -20,6 +17,7 @@ "dayjs": "^1.11.10", "dotenv": "^16.4.5", "sharp": "^0.32.6", + "ts-node": "^10.9.2", "typescript": "^5.2.2", "uuid": "^9.0.0" } @@ -57,6 +55,7 @@ "version": "0.8.1", "resolved": "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -68,6 +67,7 @@ "version": "0.3.9", "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -86,6 +86,7 @@ "version": "3.1.1", "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -93,27 +94,32 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true }, "node_modules/@types/cheerio": { "version": "0.22.35", @@ -128,6 +134,7 @@ "version": "20.10.0", "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.10.0.tgz", "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", + "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -142,6 +149,7 @@ "version": "8.11.3", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -153,6 +161,7 @@ "version": "8.3.2", "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -266,7 +275,8 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "node_modules/css-select": { "version": "5.1.0", @@ -330,6 +340,7 @@ "version": "4.0.2", "resolved": "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, "engines": { "node": ">=0.3.1" } @@ -482,7 +493,8 @@ "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "node_modules/mimic-response": { "version": "3.1.0", @@ -807,6 +819,7 @@ "version": "10.9.2", "resolved": "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -848,7 +861,8 @@ "node_modules/ts-node/node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "node_modules/tunnel": { "version": "0.0.6", @@ -875,6 +889,7 @@ "version": "5.3.2", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.2.tgz", "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -898,7 +913,8 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -918,7 +934,8 @@ "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, "node_modules/wrappy": { "version": "1.0.2", @@ -936,6 +953,7 @@ "version": "3.1.1", "resolved": "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 19222c92..435192cc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "scripts": { "get:new": "ts-node script/index.ts", "pure": "rm -rf node_modules", - "gh-fetch": "node -r ts-node/register --env-file=.env ./script/index.ts" + "gh-fetch": "node -r ts-node/register --env-file=.env ./script/index.ts", + "get-video": "node -r ts-node/register ./script/fetch-video/index.ts" }, "description": "睡前消息文稿合集", "license": "MIT", @@ -18,9 +19,9 @@ "dotenv": "^16.4.5", "sharp": "^0.32.6", "typescript": "^5.2.2", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "ts-node": "^10.9.2" }, "dependencies": { - "ts-node": "^10.9.2" } } diff --git a/script/fetch-video/index.ts b/script/fetch-video/index.ts new file mode 100644 index 00000000..298af6bb --- /dev/null +++ b/script/fetch-video/index.ts @@ -0,0 +1,101 @@ + +import * as ghac from '@actions/core'; +import fs from 'fs' +const regex =/【(睡前消息|高见|讲点黑话)(\d{2,4})】(.+)/ +const categoryMap = { + '睡前消息': 'btnews', + '高见': 'opinion', + '讲点黑话': 'commercial' +} + +const isGitHubAction = process.env.GITHUB_ACTIONS + + +const program = new Command(); +function getOptions() { + program + .option('--bv [string]') + .option('--category [string]', 'category'); + const options = program.opts(); + program.parse(process.argv); + const bv = options.bv!==""?(typeof options.bv == "boolean" ?undefined:options.bv):undefined; + const category = options.catagory!==""?(typeof options.bv == "boolean" ?undefined:options.catagory):undefined; + return { + bv, + category + } +} + +export const options = getOptions() +console.log(options) + +const categoryToBiliMap = { + 'btnews': { + mid: '316568752', + keywords: '睡前消息' + }, + 'opinion': { + mid: '59104725', + keywords: '高见' + }, + 'commercial': { + mid: '64219557', + keywords: '讲点黑话' + } +} as const + +async function getNewestVideoByCategory(category?: (keyof typeof categoryToBiliMap)) { + let _category = category ?? 'btnews' + const res = await fetch(`https://api.bilibili.com/x/series/recArchivesByKeywords?mid=${categoryToBiliMap[_category].mid}&keywords=${categoryToBiliMap[_category].keywords}`) + const data = await res.json() + const video = data.data.archives[0] + const bvid = video.bvid + return bvid +} + +function getPathAndIndexByTitle(title:string) { + const [, type, index ] = regex.exec(title)! + const category = categoryMap[type as keyof typeof categoryMap] + const intIdx = parseInt(index) + const rest = intIdx%100 > 0 ? 0 : 1 + const start = (Math.floor(intIdx/100) * 100 + rest).toString().padStart(4, '0') + const end = (Math.ceil(intIdx/100) * 100).toString().padStart(4, '0') + const current = intIdx.toString().padStart(4, '0') + const p = `docs/btnews/${category}/${start}_${end}/${category}_${current}.md` + return { + p, + index: intIdx, + category + } +} + +async function main() { + // options + let bvid = options.bv + + if(!options.bv) { + bvid = await getNewestVideoByCategory(options.category) + } + + const detail = await fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`).then(res => res.json()) + const title = detail.data.title + const description = detail.data.desc + const pubtime = detail.data.pubdate // timestamp 10 + const {p, category, index} = getPathAndIndexByTitle(title) + // const index 889 + let exist = false + if(fs.existsSync(p)) { + exist = true + } + ghac.setOutput('exist', exist); + ghac.setOutput('filepath', p); + ghac.setOutput('index', index); + ghac.setOutput('title', title); + ghac.setOutput('bvid', bvid); + ghac.setOutput('date', pubtime); + ghac.setOutput('description', description); + ghac.setOutput('category', category); +} + + +main() \ No newline at end of file