WIP(thirdparty): allow manual trigger of the plugins hook
This commit is contained in:
parent
1e714dd28e
commit
2d0a8676e4
|
@ -9326,6 +9326,36 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwner'
|
||||
description: ''
|
||||
/api/v1/uploads/trigger-download/:
|
||||
post:
|
||||
operationId: create_upload_trigger_download
|
||||
tags:
|
||||
- uploads
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
application/activity+json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
required: true
|
||||
security:
|
||||
- oauth2: []
|
||||
- ApplicationToken: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwner'
|
||||
description: ''
|
||||
/api/v1/users/{username}/:
|
||||
put:
|
||||
operationId: update_user
|
||||
|
@ -19007,6 +19037,36 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwner'
|
||||
description: ''
|
||||
/api/v2/uploads/trigger-download/:
|
||||
post:
|
||||
operationId: create_upload_trigger_download_2
|
||||
tags:
|
||||
- uploads
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
application/activity+json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwnerRequest'
|
||||
required: true
|
||||
security:
|
||||
- oauth2: []
|
||||
- ApplicationToken: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UploadForOwner'
|
||||
description: ''
|
||||
/api/v2/users/{username}/:
|
||||
put:
|
||||
operationId: update_user_3
|
||||
|
@ -24767,6 +24827,10 @@ components:
|
|||
default: pending
|
||||
privacy_level:
|
||||
$ref: '#/components/schemas/LibraryPrivacyLevelEnum'
|
||||
third_party_provider:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 100
|
||||
import_metadata:
|
||||
$ref: '#/components/schemas/ImportMetadataRequest'
|
||||
import_reference:
|
||||
|
@ -25908,6 +25972,10 @@ components:
|
|||
default: pending
|
||||
privacy_level:
|
||||
$ref: '#/components/schemas/LibraryPrivacyLevelEnum'
|
||||
third_party_provider:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 100
|
||||
import_details:
|
||||
readOnly: true
|
||||
import_metadata:
|
||||
|
@ -25951,6 +26019,10 @@ components:
|
|||
default: pending
|
||||
privacy_level:
|
||||
$ref: '#/components/schemas/LibraryPrivacyLevelEnum'
|
||||
third_party_provider:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 100
|
||||
import_metadata:
|
||||
$ref: '#/components/schemas/ImportMetadataRequest'
|
||||
import_reference:
|
||||
|
|
|
@ -9,5 +9,5 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@plugins.register_hook(plugins.TRIGGER_THIRD_PARTY_UPLOAD, PLUGIN)
|
||||
def lauch_download(track, conf={}):
|
||||
def lauch_download(track, actor, conf={}):
|
||||
tasks.archive_download.delay(track_id=track.pk, conf=conf)
|
||||
|
|
|
@ -53,7 +53,7 @@ def check_last_third_party_queries(track, count):
|
|||
check_last_third_party_queries(track, count)
|
||||
|
||||
|
||||
def create_upload(url, track, files_data):
|
||||
def get_or_create_upload(url, track, files_data):
|
||||
mimetype = f"audio/{files_data.get('format', 'unknown')}"
|
||||
duration = files_data.get("mtime", 0)
|
||||
filesize = files_data.get("size", 0)
|
||||
|
@ -64,19 +64,23 @@ def create_upload(url, track, files_data):
|
|||
actor=actors.get_service_actor(),
|
||||
)
|
||||
|
||||
return models.Upload.objects.create(
|
||||
defaults = {
|
||||
"creation_date": timezone.now(),
|
||||
"duration": duration,
|
||||
"size": filesize,
|
||||
"bitrate": bitrate,
|
||||
"import_status": "pending",
|
||||
}
|
||||
upload, created = models.Upload.objects.get_or_create(
|
||||
defaults=defaults,
|
||||
mimetype=mimetype,
|
||||
source=url,
|
||||
third_party_provider="archive-dl",
|
||||
creation_date=timezone.now(),
|
||||
track=track,
|
||||
duration=duration,
|
||||
size=filesize,
|
||||
bitrate=bitrate,
|
||||
library=service_library,
|
||||
from_activity=None,
|
||||
import_status="pending",
|
||||
)
|
||||
return upload
|
||||
|
||||
|
||||
@celery.app.task(name="archivedl.archive_download")
|
||||
|
@ -89,9 +93,11 @@ def archive_download(track, conf):
|
|||
logger.error(e)
|
||||
return
|
||||
artist_name = utils.get_artist_credit_string(track)
|
||||
# a lot of times this don't find anything, archive relies more on albums than tracks
|
||||
query = f"mediatype:audio AND title:{track.title} AND creator:{artist_name}"
|
||||
with requests.Session() as session:
|
||||
url = get_search_url(query, page_size=1, page=1)
|
||||
get_or_create_upload(url, track, files_data={})
|
||||
page_data = fetch_json(url, session)
|
||||
for obj in page_data["response"]["docs"]:
|
||||
logger.info(f"launching download item for {str(obj)}")
|
||||
|
@ -123,7 +129,7 @@ def download_item(
|
|||
)
|
||||
)
|
||||
url = f"https://archive.org/download/{item_data['identifier']}/{to_download[0]['name']}"
|
||||
upload = create_upload(url, track, to_download[0])
|
||||
upload = get_or_create_upload(url, track, to_download[0])
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
path = os.path.join(temp_dir, to_download[0]["name"])
|
||||
|
|
|
@ -399,6 +399,7 @@ class UploadSerializer(serializers.ModelSerializer):
|
|||
"import_date",
|
||||
"import_status",
|
||||
"privacy_level",
|
||||
"third_party_provider",
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
|
|
|
@ -18,6 +18,7 @@ from rest_framework import views, viewsets
|
|||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from config import plugins
|
||||
from funkwhale_api.common import decorators as common_decorators
|
||||
from funkwhale_api.common import permissions as common_permissions
|
||||
from funkwhale_api.common import preferences
|
||||
|
@ -820,6 +821,27 @@ class UploadViewSet(
|
|||
status=200,
|
||||
)
|
||||
|
||||
@action(methods=["post"], detail=False, url_path="trigger-download")
|
||||
def trigger_download(self, request, *args, **kwargs):
|
||||
qs = self.get_queryset()
|
||||
track = models.Track.objects.get(pk=request.data["track"])
|
||||
actor = utils.get_actor_from_request(self.request)
|
||||
tp_upload = models.Upload.objects.filter(
|
||||
track=track, third_party_provider__isnull=False
|
||||
)
|
||||
if tp_upload.exists():
|
||||
return Response(
|
||||
serializers.UploadSerializer(tp_upload.first()).data,
|
||||
status=200,
|
||||
)
|
||||
if not qs.filter(track=track).exists():
|
||||
plugins.trigger_hook(
|
||||
plugins.TRIGGER_THIRD_PARTY_UPLOAD,
|
||||
track=track,
|
||||
actor=actor,
|
||||
)
|
||||
return Response(status=404)
|
||||
|
||||
@action(methods=["post"], detail=False)
|
||||
def action(self, request, *args, **kwargs):
|
||||
queryset = self.get_queryset()
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/
|
|||
import type { components } from '~/generated/types'
|
||||
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
|
||||
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import usePlayOptions from '~/composables/audio/usePlayOptions'
|
||||
import useReport from '~/composables/moderation/useReport'
|
||||
|
@ -14,6 +14,7 @@ import Button from '~/components/ui/Button.vue'
|
|||
import OptionsButton from '~/components/ui/button/Options.vue'
|
||||
import Popover from '~/components/ui/Popover.vue'
|
||||
import PopoverItem from '~/components/ui/popover/PopoverItem.vue'
|
||||
import axios from 'axios'
|
||||
|
||||
interface Props extends PlayOptionsProps {
|
||||
split?: boolean
|
||||
|
@ -103,10 +104,36 @@ const labels = computed(() => ({
|
|||
PlaylistUploadGranted: t('components.audio.PlayButton.button.PlaylistUploadGranted'),
|
||||
PlaylistUploadPending:t('components.audio.PlayButton.button.PlaylistUploadPending'),
|
||||
PlaylistUploadNotRequest: t('components.audio.PlayButton.button.PlaylistUploadNotRequest'),
|
||||
PlaylistUploadTooltip: t('components.audio.PlayButton.button.PlaylistUploadTooltip')
|
||||
PlaylistUploadTooltip: t('components.audio.PlayButton.button.PlaylistUploadTooltip'),
|
||||
thirdPartyDownload: t('components.audio.PlayButton.button.thirdPartyDownload'),
|
||||
thirdPartyDownloadTooltip: t('components.audio.PlayButton.button.thirdPartyDownload'),
|
||||
thirdPartyDownloadTriggered: t('components.audio.PlayButton.button.thirdPartyDownloadTriggered')
|
||||
|
||||
}))
|
||||
|
||||
const isOpen = ref(false)
|
||||
const uploads = ref<components['schemas']['UploadForOwner'][]>([])
|
||||
|
||||
const thirdPartyUpload = computed(() => {
|
||||
if (uploads.value?.find(upload => 'third_party_provider' in upload)) { return false } else { return true }
|
||||
})
|
||||
|
||||
const triggerThirdPartyHook = async () => {
|
||||
const response = await axios.post(
|
||||
'uploads/trigger-download',
|
||||
{ track: props.track?.id }
|
||||
);
|
||||
|
||||
if (response.status === 404) {
|
||||
uploads.value.push({ third_party_upload: "triggered" });
|
||||
return null;
|
||||
} else if (response.status === 200) {
|
||||
console.log("uploads 400 (response.dat ", response.data)
|
||||
uploads.value.push(response.data)
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
triggerThirdPartyHook()
|
||||
|
||||
const playlistLibraryFollowInfo = computed(() => {
|
||||
const playlist = props.playlist;
|
||||
|
@ -246,7 +273,22 @@ const playlistLibraryFollowInfo = computed(() => {
|
|||
{{ t('components.audio.PlayButton.button.trackDetails') }}
|
||||
</span>
|
||||
</PopoverItem>
|
||||
|
||||
<PopoverItem
|
||||
v-if="!playable && thirdPartyUpload"
|
||||
:title="labels.thirdPartyDownloadTooltip"
|
||||
icon="bi-cloud-download"
|
||||
@click.stop.prevent="triggerThirdPartyHook();"
|
||||
>
|
||||
{{ labels.thirdPartyDownload }}
|
||||
</PopoverItem>
|
||||
<PopoverItem
|
||||
v-else-if="!playable && !thirdPartyUpload"
|
||||
:title="labels.thirdPartyDownloadTooltip"
|
||||
:disabled="true"
|
||||
icon="bi-cloud-download"
|
||||
>
|
||||
{{ labels.thirdPartyDownloadTriggered }}
|
||||
</PopoverItem>
|
||||
<hr v-if="filterableArtist || Object.keys(getReportableObjects({ track, album, artist, playlist, account, channel })).length > 0">
|
||||
|
||||
<PopoverItem
|
||||
|
|
|
@ -228,7 +228,6 @@ export default (props: PlayOptionsProps) => {
|
|||
return response;
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
playable,
|
||||
filterableArtist,
|
||||
|
|
|
@ -2895,6 +2895,22 @@ export interface paths {
|
|||
patch: operations["partial_update_upload_bulk_update"];
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v1/uploads/trigger-download/": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["create_upload_trigger_download"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v1/users/{username}/": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
@ -6019,6 +6035,22 @@ export interface paths {
|
|||
patch: operations["partial_update_upload_bulk_update_2"];
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v2/uploads/trigger-download/": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["create_upload_trigger_download_2"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v2/users/{username}/": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
@ -8472,6 +8504,7 @@ export interface components {
|
|||
/** @default pending */
|
||||
import_status: components["schemas"]["ImportStatusEnum"];
|
||||
privacy_level?: components["schemas"]["LibraryPrivacyLevelEnum"];
|
||||
third_party_provider?: string | null;
|
||||
import_metadata?: components["schemas"]["ImportMetadataRequest"];
|
||||
import_reference?: string;
|
||||
source?: string | null;
|
||||
|
@ -8893,6 +8926,7 @@ export interface components {
|
|||
/** @default pending */
|
||||
import_status: components["schemas"]["ImportStatusEnum"];
|
||||
privacy_level?: components["schemas"]["LibraryPrivacyLevelEnum"];
|
||||
third_party_provider?: string | null;
|
||||
readonly import_details: unknown;
|
||||
import_metadata?: components["schemas"]["ImportMetadata"];
|
||||
import_reference?: string;
|
||||
|
@ -8907,6 +8941,7 @@ export interface components {
|
|||
/** @default pending */
|
||||
import_status: components["schemas"]["ImportStatusEnum"];
|
||||
privacy_level?: components["schemas"]["LibraryPrivacyLevelEnum"];
|
||||
third_party_provider?: string | null;
|
||||
import_metadata?: components["schemas"]["ImportMetadataRequest"];
|
||||
import_reference?: string;
|
||||
source?: string | null;
|
||||
|
@ -15758,6 +15793,32 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
create_upload_trigger_download: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["UploadForOwnerRequest"];
|
||||
"application/x-www-form-urlencoded": components["schemas"]["UploadForOwnerRequest"];
|
||||
"multipart/form-data": components["schemas"]["UploadForOwnerRequest"];
|
||||
"application/activity+json": components["schemas"]["UploadForOwnerRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["UploadForOwner"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
update_user: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
@ -22917,6 +22978,32 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
create_upload_trigger_download_2: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["UploadForOwnerRequest"];
|
||||
"application/x-www-form-urlencoded": components["schemas"]["UploadForOwnerRequest"];
|
||||
"multipart/form-data": components["schemas"]["UploadForOwnerRequest"];
|
||||
"application/activity+json": components["schemas"]["UploadForOwnerRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["UploadForOwner"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
update_user_3: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
|
@ -492,7 +492,10 @@
|
|||
"playTracks": "Play tracks",
|
||||
"report": "Report…",
|
||||
"startRadio": "Play similar songs",
|
||||
"trackDetails": "Track details"
|
||||
"trackDetails": "Track details",
|
||||
"thirdPartyDownload": "Try to download from third-party service",
|
||||
"thirdPartyDownloadTooltip": "This relies on installed plugins and may not work on all pods.",
|
||||
"thirdPartyDownloadTriggered": "Download triggered, come back later"
|
||||
},
|
||||
"title": {
|
||||
"more": "More…",
|
||||
|
|
Loading…
Reference in New Issue