Merge pull request #200 from arabcoders/dev

Rely on native video player
This commit is contained in:
Abdulmohsen 2025-03-05 22:18:35 +03:00 committed by GitHub
commit 73f97b80f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 771 additions and 783 deletions

60
Pipfile.lock generated
View file

@ -27,11 +27,11 @@
},
"aiohappyeyeballs": {
"hashes": [
"sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1",
"sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0"
"sha256:19728772cb12263077982d2f55453babd8bec6a052a926cd5c0c42796da8bf62",
"sha256:6cac4f5dd6e34a9644e69cf9021ef679e4394f54e58a183056d12009e42ea9e3"
],
"markers": "python_version >= '3.9'",
"version": "==2.4.6"
"version": "==2.4.8"
},
"aiohttp": {
"hashes": [
@ -453,36 +453,36 @@
},
"debugpy": {
"hashes": [
"sha256:086b32e233e89a2740c1615c2f775c34ae951508b28b308681dbbb87bba97d06",
"sha256:22a11c493c70413a01ed03f01c3c3a2fc4478fc6ee186e340487b2edcd6f4180",
"sha256:274b6a2040349b5c9864e475284bce5bb062e63dce368a394b8cc865ae3b00c6",
"sha256:2ae5df899732a6051b49ea2632a9ea67f929604fd2b036613a9f12bc3163b92d",
"sha256:36f4829839ef0afdfdd208bb54f4c3d0eea86106d719811681a8627ae2e53dd5",
"sha256:39dfbb6fa09f12fae32639e3286112fc35ae976114f1f3d37375f3130a820969",
"sha256:4703575b78dd697b294f8c65588dc86874ed787b7348c65da70cfc885efdf1e1",
"sha256:4ad9a94d8f5c9b954e0e3b137cc64ef3f579d0df3c3698fe9c3734ee397e4abb",
"sha256:557cc55b51ab2f3371e238804ffc8510b6ef087673303890f57a24195d096e61",
"sha256:5cc45235fefac57f52680902b7d197fb2f3650112379a6fa9aa1b1c1d3ed3f02",
"sha256:646530b04f45c830ceae8e491ca1c9320a2d2f0efea3141487c82130aba70dce",
"sha256:696d8ae4dff4cbd06bf6b10d671e088b66669f110c7c4e18a44c43cf75ce966f",
"sha256:7e94b643b19e8feb5215fa508aee531387494bf668b2eca27fa769ea11d9f498",
"sha256:88a77f422f31f170c4b7e9ca58eae2a6c8e04da54121900651dfa8e66c29901a",
"sha256:898fba72b81a654e74412a67c7e0a81e89723cfe2a3ea6fcd3feaa3395138ca9",
"sha256:9649eced17a98ce816756ce50433b2dd85dfa7bc92ceb60579d68c053f98dff9",
"sha256:9af40506a59450f1315168d47a970db1a65aaab5df3833ac389d2899a5d63b3f",
"sha256:a28ed481d530e3138553be60991d2d61103ce6da254e51547b79549675f539b7",
"sha256:a2ba7ffe58efeae5b8fad1165357edfe01464f9aef25e814e891ec690e7dd82a",
"sha256:a4042edef80364239f5b7b5764e55fd3ffd40c32cf6753da9bda4ff0ac466018",
"sha256:b0232cd42506d0c94f9328aaf0d1d0785f90f87ae72d9759df7e5051be039738",
"sha256:b202f591204023b3ce62ff9a47baa555dc00bb092219abf5caf0e3718ac20e7c",
"sha256:b5c6c967d02fee30e157ab5227706f965d5c37679c687b1e7bbc5d9e7128bd41",
"sha256:cbbd4149c4fc5e7d508ece083e78c17442ee13b0e69bfa6bd63003e486770f45",
"sha256:f30b03b0f27608a0b26c75f0bb8a880c752c0e0b01090551b9d87c7d783e2069",
"sha256:fdb3c6d342825ea10b90e43d7f20f01535a72b3a1997850c0c3cefa5c27a4a2c"
"sha256:06859f68e817966723ffe046b896b1bd75c665996a77313370336ee9e1de3e90",
"sha256:2b8de94c5c78aa0d0ed79023eb27c7c56a64c68217d881bee2ffbcb13951d0c1",
"sha256:31abc9618be4edad0b3e3a85277bc9ab51a2d9f708ead0d99ffb5bb750e18503",
"sha256:32b6857f8263a969ce2ca098f228e5cc0604d277447ec05911a8c46cf3e7e307",
"sha256:3872ce5453b17837ef47fb9f3edc25085ff998ce63543f45ba7af41e7f7d370f",
"sha256:3dae443739c6b604802da9f3e09b0f45ddf1cf23c99161f3a1a8039f61a8bb89",
"sha256:46abe0b821cad751fc1fb9f860fb2e68d75e2c5d360986d0136cd1db8cad4428",
"sha256:4caca674206e97c85c034c1efab4483f33971d4e02e73081265ecb612af65377",
"sha256:5268ae7fdca75f526d04465931cb0bd24577477ff50e8bb03dab90983f4ebd02",
"sha256:62f9b4a861c256f37e163ada8cf5a81f4c8d5148fc17ee31fb46813bd658cdcc",
"sha256:63ca7670563c320503fea26ac688988d9d6b9c6a12abc8a8cf2e7dd8e5f6b6ea",
"sha256:6fab771639332bd8ceb769aacf454a30d14d7a964f2012bf9c4e04c60f16e85b",
"sha256:79ce4ed40966c4c1631d0131606b055a5a2f8e430e3f7bf8fd3744b09943e8e8",
"sha256:7d9a05efc6973b5aaf076d779cf3a6bbb1199e059a17738a2aa9d27a53bcc888",
"sha256:837e7bef95bdefba426ae38b9a94821ebdc5bea55627879cd48165c90b9e50ce",
"sha256:887d54276cefbe7290a754424b077e41efa405a3e07122d8897de54709dbe522",
"sha256:924464d87e7d905eb0d79fb70846558910e906d9ee309b60c4fe597a2e802590",
"sha256:a0bd87557f97bced5513a74088af0b84982b6ccb2e254b9312e29e8a5c4270eb",
"sha256:acf39a6e98630959763f9669feddee540745dfc45ad28dbc9bd1f9cd60639391",
"sha256:cb56c2db69fb8df3168bc857d7b7d2494fed295dfdbde9a45f27b4b152f37520",
"sha256:d4ba115cdd0e3a70942bd562adba9ec8c651fe69ddde2298a1be296fc331906f",
"sha256:dc7b77f5d32674686a5f06955e4b18c0e41fb5a605f5b33cf225790f114cfeec",
"sha256:ea869fe405880327497e6945c09365922c79d2a1eed4c3ae04d77ac7ae34b2b5",
"sha256:ed93c3155fc1f888ab2b43626182174e457fc31b7781cd1845629303790b8ad1",
"sha256:eee02b2ed52a563126c97bf04194af48f2fe1f68bb522a312b05935798e922ff",
"sha256:f14d2c4efa1809da125ca62df41050d9c7cd9cb9e380a2685d1e453c4d450ccb"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.8.12"
"version": "==1.8.13"
},
"frozenlist": {
"hashes": [

View file

@ -63,43 +63,44 @@ services:
- /tmp
```
## Configuration via environment variables
## Environment variables
Certain values can be set via environment variables, using the `-e` parameter on the docker command line, or the `environment:` section in `compose.yaml` file.
Certain configuration values can be set via environment variables, using the `-e` parameter on the docker command line, or the `environment:` section in `compose.yaml` file.
| Environment Variable | Description | Default |
| ------------------------ | ---------------------------------------------------------------- | ---------------------------------- |
| YTP_OUTPUT_TEMPLATE | The template for the filenames of the downloaded videos | `%(title)s.%(ext)s` |
| YTP_DEFAULT_PRESET | The default preset to use for the download | `default` |
| YTP_INSTANCE_TITLE | The title of the instance | `empty string` |
| YTP_FILE_LOGGING | Whether to log to file | `false` |
| YTP_DOWNLOAD_PATH | Path to where the downloads will be saved | `/downloads` |
| YTP_MAX_WORKERS | How many works to use for downloads | `1` |
| YTP_AUTH_USERNAME | Username for basic authentication | `empty string` |
| YTP_AUTH_PASSWORD | Password for basic authentication | `empty string` |
| YTP_REMOVE_FILES | Remove the actual file when clicking the remove button | `false` |
| YTP_CONFIG_PATH | Path to where the queue persistence files will be saved | `/config` |
| YTP_TEMP_PATH | Path where intermediary download files will be saved | `/tmp` |
| YTP_TEMP_KEEP | Whether to keep the Individual video temp directory or remove it | `false` |
| YTP_KEEP_ARCHIVE | Keep history of downloaded videos | `true` |
| YTP_YTDL_DEBUG | Whether to turn debug logging for the internal `yt-dlp` package | `false` |
| YTP_ALLOW_MANIFESTLESS | Allow `yt-dlp` to download unprocessed streams | `false` |
| YTP_HOST | Which IP address to bind to | `0.0.0.0` |
| YTP_PORT | Which port to bind to | `8081` |
| YTP_LOG_LEVEL | Log level | `info` |
| YTP_STREAMER_VCODEC | The video codec to use for in-browser streaming | `libx264` |
| YTP_STREAMER_ACODEC | The audio codec to use for in-browser streaming | `aac` |
| YTP_ACCESS_LOG | Whether to log access to the web server | `true` |
| YTP_DEBUG | Whether to turn on debug mode | `false` |
| YTP_DEBUGPY_PORT | The port to use for the debugpy debugger | `5678` |
| YTP_SOCKET_TIMEOUT | The timeout for the yt-dlp socket connection variable | `30` |
| YTP_EXTRACT_INFO_TIMEOUT | The timeout for extracting video information | `70` |
| YTP_DB_FILE | The path to the SQLite database file | `{config_path}/ytptube.db` |
| YTP_MANUAL_ARCHIVE | The path to the manual archive file | `{config_path}/manual_archive.log` |
| YTP_UI_UPDATE_TITLE | Whether to update the title of the page with the current stats | `true` |
| YTP_PIP_PACKAGES | A space separated list of pip packages to install | `empty string` |
| YTP_PIP_IGNORE_UPDATES | Do not update the custom pip packages | `false` |
| YTP_BASIC_MODE | Whether to run WebUI in basic mode | `false` |
* __YTP_CONFIG_PATH__: path to where the queue persistence files will be saved. Defaults to `/config` in the docker image, and `./var/config` otherwise.
* __YTP_DOWNLOAD_PATH__: path to where the downloads will be saved. Defaults to `/downloads` in the docker image, and `./var/downloads` otherwise.
* __YTP_TEMP_PATH__: path where intermediary download files will be saved. Defaults to `/tmp` in the docker image, and `./var/tmp` otherwise.
* __YTP_TEMP_KEEP__: Whether to keep the Individual video temp directory or remove it. Defaults to `false`.
* __YTP_OUTPUT_TEMPLATE__: the template for the filenames of the downloaded videos, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s.%(ext)s`. This will be the default for all downloads unless the request include output template.
* __YTP_OUTPUT_TEMPLATE_CHAPTER__: the template for the filenames of the downloaded videos, when split into chapters via postprocessors, formatted according to [this spec](https://github.com/yt-dlp/yt-dlp/blob/master/README.md#output-template). Defaults to `%(title)s - %(section_number)s %(section_title)s.%(ext)s.`
* __YTP_KEEP_ARCHIVE__: Whether to keep history of downloaded videos to prevent downloading same file multiple times. Defaults to `true`.
* __YTP_YTDL_DEBUG__: Whether to turn debug logging for the internal `yt-dlp` package. Defaults to `false`.
* __YTP_ALLOW_MANIFESTLESS__: Allow `yt-dlp` to download live streams videos which are yet to be processed by YouTube. Defaults to `false`
* __YTP_HOST__: Which IP address to bind to. Defaults to `0.0.0.0`.
* __YTP_PORT__: Which port to bind to. Defaults to `8081`.
* __YTP_LOG_LEVEL__: Log level. Defaults to `info`.
* __YTP_MAX_WORKERS__: How many works to use for downloads. Defaults to `1`.
* __YTP_STREAMER_VCODEC__: The video codec to use for in-browser streaming. Defaults to `libx264`.
* __YTP_STREAMER_ACODEC__: The audio codec to use for in-browser streaming. Defaults to `aac`.
* __YTP_AUTH_USERNAME__: Username for basic authentication. Defaults open for all
* __YTP_AUTH_PASSWORD__: Password for basic authentication. Defaults open for all.
* __YTP_REMOVE_FILES__: Whether to remove the actual downloaded file when clicking the remove button. Defaults to `false`.
* __YTP_ACCESS_LOG__: Whether to log access to the web server. Defaults to `true`.
* __YTP_DEBUG__: Whether to turn on debug mode. Defaults to `false`.
* __YTP_DEBUGPY_PORT__: The port to use for the debugpy debugger. Defaults to `5678`.
* __YTP_SOCKET_TIMEOUT__: The timeout for the yt-dlp socket connection variable. Defaults to `30`.
* __YTP_EXTRACT_INFO_TIMEOUT__: The timeout for extracting video information. Defaults to `70`.
* __YTP_DB_FILE__: The path to the SQLite database file. Defaults to `{config_path}/ytptube.db`.
* __YTP_MANUAL_ARCHIVE__: The path to the manual archive file. Defaults to `{config_path}/manual_archive.log`.
* __YTP_UI_UPDATE_TITLE__: Whether to update the title of the page with the current stats. Defaults to `true`.
* __YTP_PIP_PACKAGES__: a space separated list of pip packages to install. Defaults to `""`, you can also use `{config_path}/pip.txt` to install the packages.
* __YTP_PIP_IGNORE_UPDATES__: Do not update the custom pip packages. Defaults to `false`.
* __YTP_BASIC_MODE__: Whether to run WebUI in basic mode. Defaults to `false`. In basic mode, A minimal UI will be shown, the majority of the features will be disabled.
* __YTP_DEFAULT_PRESET__: The default preset to use for the download. Defaults to `default`.
* __YTP_INSTANCE_TITLE__: The title of the instance. Defaults to empty string.
* __YTP_FILE_LOGGING__: Whether to log to file. Defaults to `false`.
## Running behind a reverse proxy
It's advisable to run YTPTube behind a reverse proxy, if authentication and/or HTTPS support are required.
@ -242,7 +243,7 @@ my jp video downloads is:
```
You can convert your own yt-dlp command arguments into a preset using the box found in the presets add page. For reference,
The options can be fount at [yt-dlp YoutubeDL.py](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L214) file.
The options can be found at [yt-dlp YoutubeDL.py](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L214) file.
And for the postprocessors at [yt-dlp postprocessor](https://github.com/yt-dlp/yt-dlp/tree/master/yt_dlp/postprocessor).
## Authentication

View file

@ -1,45 +1,36 @@
<style>
:root {
--plyr-captions-background: rgba(0, 0, 0, 0.6);
--plyr-captions-text-color: #f3db4d;
--webkit-text-track-display: none;
}
.plyr__caption {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
font-size: 140%;
font-weight: bold;
}
.plyr--full-ui ::-webkit-media-text-track-container {
display: var(--webkit-text-track-display);
<style scoped>
.player {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
</style>
<template>
<div>
<video id="player" ref="video" :poster="thumbnail" :title="title" playsinline>
<video class="player" ref="video" :poster="thumbnail" :title="title" playsinline controls crossorigin="anonymous"
preload="auto">
<source v-for="source in sources" :key="source.src" :src="source.src" @error="source.onerror"
:type="source.type" />
<track v-for="track in tracks" :key="track.file" :kind="track.kind" :label="track.label" :srclang="track.lang"
:src="track.file" default />
<track v-for="(track, i) in tracks" :key="track.file" :kind="track.kind" :label="track.label" :srclang="track.lang"
:src="track.file" :default="notFirefox && i === 0" />
</video>
</div>
</template>
<script setup>
import { useStorage } from '@vueuse/core'
import { onMounted, onUpdated, ref, onUnmounted } from 'vue'
import Hls from 'hls.js'
import Plyr from 'plyr'
import 'plyr/dist/plyr.css'
import { makeDownload } from '~/utils/index'
const config = useConfigStore()
const props = defineProps({
item: {
type: Object,
default: () => ({})
},
default: () => ({}),
}
})
const emitter = defineEmits(['closeModel'])
@ -48,12 +39,14 @@ const video = ref(null)
const tracks = ref([])
const sources = ref([])
const thumbnail = ref('')
const thumbnail = ref('/images/placeholder.png')
const artist = ref('')
const title = ref('')
const isAudio = ref(false)
const isApple = /(iPhone|iPod|iPad).*AppleWebKit/i.test(navigator.userAgent)
const volume = useStorage('player_volume', 1)
const notFirefox = !navigator.userAgent.toLowerCase().includes('firefox')
let player = null;
let hls = null;
const eventFunc = e => {
@ -63,13 +56,20 @@ const eventFunc = e => {
}
onMounted(async () => {
const isApple = /(iPhone|iPod|iPad).*AppleWebKit/i.test(navigator.userAgent)
video.value.volume = volume.value
video.value.addEventListener('volumechange', () => {
volume.value = video.value.volume
})
const response = await (await request(makeDownload(config, props.item, 'api/file/info'))).json()
if (props.item.extras?.thumbnail) {
thumbnail.value = '/api/thumbnail?url=' + encodePath(props.item.extras.thumbnail)
}
// -- check if mimetype is video/mp4 and device is apple
// -- as always apple, apple like to be snowflakes.
if (isApple) {
@ -123,9 +123,6 @@ onMounted(async () => {
onUpdated(() => prepareVideoPlayer())
onUnmounted(() => {
if (player) {
player.destroy()
}
if (hls) {
hls.destroy()
}
@ -138,62 +135,23 @@ onUnmounted(() => {
})
const prepareVideoPlayer = () => {
if (false === ("mediaSession" in navigator)) {
return
}
let mediaMetadata = {
title: title.value,
};
if (thumbnail.value) {
mediaMetadata['artwork'] = [
{ src: thumbnail.value, sizes: '1920x1080', type: 'image/jpeg' },
]
mediaMetadata.artwork = [{ src: thumbnail.value, sizes: '1920x1080', type: 'image/jpeg' }]
}
if (artist.value) {
mediaMetadata['artist'] = artist.value
}
let opts = {
debug: false,
clickToPlay: true,
keyboard: { focused: true, global: true },
controls: [
'play-large', 'play', 'progress', 'current-time', 'duration', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'
],
fullscreen: {
enabled: true,
fallback: true,
iosNative: true,
},
storage: {
enabled: true,
key: 'plyr'
},
artist: artist.value,
mediaMetadata: mediaMetadata,
captions: {
update: true,
},
};
if (artist.value) {
opts.artist = artist.value
mediaMetadata.artist = artist.value
}
if (title.value) {
opts.title = title.value
}
if (thumbnail.value) {
opts.poster = thumbnail.value
}
player = new Plyr(video.value, opts);
player.source = {
type: isAudio.value ? 'audio' : 'video',
title: title.value,
poster: thumbnail.value,
};
navigator.mediaSession.metadata = new MediaMetadata(mediaMetadata);
if (title.value) {
window.document.title = `YTPTube - Playing: ${title.value}`
}
@ -212,7 +170,7 @@ const attach_hls = link => {
debug: false,
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90,
backBufferLength: 120,
fragLoadingTimeOut: 200000,
});

View file

@ -24,7 +24,6 @@
"moment": "^2.30.1",
"nuxt": "^3.11.2",
"pinia": "^2.1.7",
"plyr": "^3.7.8",
"socket.io-client": "^4.7.2",
"vue": "^3.4.21",
"vue-router": "^4.3.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 306 KiB

View file

@ -1,7 +1,7 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": false
}
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": false
}
}

File diff suppressed because it is too large Load diff