Add support for converting youtube handle channels urls to channel_id. Closes #353
This commit is contained in:
parent
7b9144b200
commit
781b767879
1 changed files with 64 additions and 32 deletions
|
|
@ -1,20 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<main class="columns mt-2 is-multiline">
|
<main class="columns mt-2 is-multiline">
|
||||||
<div class="column is-12" v-if="form?.url && is_yt_handle(form.url)">
|
<div class="column is-12" v-if="form?.url && is_yt_handle(form.url)">
|
||||||
<Message title="Warning" class="is-background-warning-80 has-text-dark" icon="fas fa-info-circle">
|
<Message title="Information" class="is-info is-background-info-80 has-text-dark" icon="fas fa-info-circle">
|
||||||
<span>
|
<span>You are using a YouTube link with <b>@handle</b> instead of <b>channel_id</b>. To activate RSS feed
|
||||||
<ul>
|
support for URL click on the <NuxtLink @click="async () => form.url = await convert_url(form.url)"><b>Convert
|
||||||
<li>You are using a YouTube link with handle instead of channel_id. To activate RSS feed support for
|
URL</b></NuxtLink> link.</span>
|
||||||
channel, you need to use
|
|
||||||
the channel ID. For example, <code>https://www.youtube.com/channel/UCUi3_cffYenmMTuWEsLHzqg</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
To get youtube channel_id simply visit the page, click on <b>more about this channel</b>, scroll down to
|
|
||||||
<b>share
|
|
||||||
channel</b>, click on <code>Copy channel id</code>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</span>
|
|
||||||
</Message>
|
</Message>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-12">
|
<div class="column is-12">
|
||||||
|
|
@ -79,7 +69,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>The name is used to identify this specific task.</span>
|
<span class="is-bold">The name is used to identify this specific task.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -89,17 +79,20 @@
|
||||||
<label class="label is-inline" for="url">
|
<label class="label is-inline" for="url">
|
||||||
<span class="icon"><i class="fa-solid fa-link" /></span>
|
<span class="icon"><i class="fa-solid fa-link" /></span>
|
||||||
URL
|
URL
|
||||||
|
<template v-if="is_yt_handle(form.url)">
|
||||||
|
- <NuxtLink @click="async () => form.url = await convert_url(form.url)">Convert URL</NuxtLink>
|
||||||
|
</template>
|
||||||
</label>
|
</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<input type="url" class="input" id="url" v-model="form.url" :disabled="addInProgress">
|
<input type="url" class="input" id="url" v-model="form.url"
|
||||||
<span class="icon is-small is-left"><i class="fa-solid fa-link" /></span>
|
:disabled="addInProgress || convertInProgress"
|
||||||
|
placeholder="https://www.youtube.com/channel/UCUi3_cffYenmMTuWEsLHzqg">
|
||||||
|
<span class="icon is-small is-left"><i class="fa-solid fa-link"
|
||||||
|
:class="{ 'fa-spin': convertInProgress }" /></span>
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>The channel or playlist URL. For youtube there is rss feed support if you use URL with
|
<span class="is-bold">The channel or playlist URL.</span>
|
||||||
channel_id or playlist_id. For example, https://www.youtube.com/<span
|
|
||||||
class="has-text-danger">channel/UCUi3_cffYenmMTuWEsLHzqg</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -130,7 +123,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>Select the preset to use for this URL. <span class="text-has-danger">If the
|
<span class="is-bold">Select the preset to use for this URL. <span class="text-has-danger">If the
|
||||||
<code>-f, --format</code> <span class="has-text-danger">
|
<code>-f, --format</code> <span class="has-text-danger">
|
||||||
argument is present in the command line options, the preset and all
|
argument is present in the command line options, the preset and all
|
||||||
it's options will be ignored.</span></span>
|
it's options will be ignored.</span></span>
|
||||||
|
|
@ -151,7 +144,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>
|
<span class="is-bold">
|
||||||
The CRON timer expression to use for this task. If not set, the task will be disabled. For more
|
The CRON timer expression to use for this task. If not set, the task will be disabled. For more
|
||||||
information on CRON expressions, see <NuxtLink to="https://crontab.guru/" target="_blank">
|
information on CRON expressions, see <NuxtLink to="https://crontab.guru/" target="_blank">
|
||||||
crontab.guru</NuxtLink>.
|
crontab.guru</NuxtLink>.
|
||||||
|
|
@ -172,8 +165,8 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>Current download folder: <code>{{ get_download_folder() }}</code>. All folders are
|
<span class="is-bold">Current download folder: <code>{{ get_download_folder() }}</code>. All folders
|
||||||
sub-folders of <code>{{ config.app.download_path }}</code>.</span>
|
are sub-folders of <code>{{ config.app.download_path }}</code>.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -190,7 +183,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>Current output format: <code>{{ get_output_template() }}</code>.</span>
|
<span class="is-bold">Current output format: <code>{{ get_output_template() }}</code>.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -210,7 +203,8 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>Whether to automatically start downloading or just queue them in paused state.</span>
|
<span class="is-bold">Whether to automatically start downloading or just queue them in paused
|
||||||
|
state.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -230,9 +224,8 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>Some URLs like YouTube channels and playlists can be monitored for new videos using RSS
|
<span class="is-bold">Some URLs like YouTube channels/playlists can be monitored using RSS feed,
|
||||||
feed independent of the timer. If enabled, the task will monitor the RSS feed for new videos and
|
this option works regardless of the timer being set or not.</span>
|
||||||
automatically queue them for download if they are not already downloaded.</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -250,7 +243,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp cli arguments. Check <NuxtLink target="_blank"
|
<span class="is-bold">yt-dlp cli arguments. Check <NuxtLink target="_blank"
|
||||||
to="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#general-options">this page</NuxtLink>.
|
to="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#general-options">this page</NuxtLink>.
|
||||||
For more info. <span class="has-text-danger">Not all options are supported some are ignored. Use
|
For more info. <span class="has-text-danger">Not all options are supported some are ignored. Use
|
||||||
with caution those arguments can break yt-dlp or the frontend.</span>
|
with caution those arguments can break yt-dlp or the frontend.</span>
|
||||||
|
|
@ -329,9 +322,10 @@ const config = useConfigStore()
|
||||||
const box = useConfirm()
|
const box = useConfirm()
|
||||||
const showImport = useStorage('showImport', false)
|
const showImport = useStorage('showImport', false)
|
||||||
|
|
||||||
|
const convertInProgress = ref<boolean>(false)
|
||||||
const import_string = ref<string>('')
|
const import_string = ref<string>('')
|
||||||
|
|
||||||
const CHANNEL_REGEX = /^https?:\/\/(?:www\.)?youtube\.com\/(?:(?:channel\/(?<channelId>UC[0-9A-Za-z_-]{22}))|(?:c\/(?<customName>[A-Za-z0-9_-]+))|(?:user\/(?<userName>[A-Za-z0-9_-]+))|(?:@(?<handle>[A-Za-z0-9_-]+)))\/?$/
|
const CHANNEL_REGEX = /^https?:\/\/(?:www\.)?youtube\.com\/(?:(?:channel\/(?<channelId>UC[0-9A-Za-z_-]{22}))|(?:c\/(?<customName>[A-Za-z0-9_-]+))|(?:user\/(?<userName>[A-Za-z0-9_-]+))|(?:@(?<handle>[A-Za-z0-9_-]+)))(?<suffix>\/.*)?\/?$/
|
||||||
|
|
||||||
const form = reactive<task_item>({ ...props.task })
|
const form = reactive<task_item>({ ...props.task })
|
||||||
|
|
||||||
|
|
@ -477,10 +471,48 @@ const get_output_template = (): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_yt_handle = (url: string): boolean => {
|
const is_yt_handle = (url: string): boolean => {
|
||||||
|
if (!url || '' === url) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
const m = url.match(CHANNEL_REGEX)
|
const m = url.match(CHANNEL_REGEX)
|
||||||
if (m?.groups) {
|
if (m?.groups) {
|
||||||
return !m.groups.channelId
|
return !m.groups.channelId
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const convert_url = async (url: string): Promise<string> => {
|
||||||
|
if (!url || '' === url) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = url.match(CHANNEL_REGEX)
|
||||||
|
if (!m?.groups || !m.groups.handle) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('url', url)
|
||||||
|
params.append('args', '-I0')
|
||||||
|
|
||||||
|
try {
|
||||||
|
convertInProgress.value = true
|
||||||
|
const resp = await request('/api/yt-dlp/url/info?' + params.toString(), { credentials: 'include' })
|
||||||
|
const body = await resp.json()
|
||||||
|
const channel_id = ag(body, 'channel_id', null)
|
||||||
|
console.log('convert_url', { url, channel_id, body })
|
||||||
|
|
||||||
|
if (channel_id) {
|
||||||
|
return url.replace(`/@${m.groups.handle}`, `/channel/${channel_id}`)
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(e)
|
||||||
|
toast.error(`Error: ${e.message}`)
|
||||||
|
} finally {
|
||||||
|
convertInProgress.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue