mirror of
https://github.com/nova-r/fediplug.git
synced 2025-02-02 06:07:27 +01:00
Add buttplugio device integration; Remove queue; Update dependencies
This is the first rudementary buttplugio integration that actually works. Use with caution. I still want to add the queue back and currently the duration and length of vibration is hard-coded.
This commit is contained in:
parent
7d18f6cf91
commit
1e1f2a40ac
7 changed files with 196 additions and 148 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -8,4 +8,5 @@
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
.vscode
|
.vscode
|
||||||
__pycache__
|
__pycache__
|
||||||
fediplay.iml
|
fediplug.iml
|
||||||
|
venv
|
|
@ -1,3 +1,3 @@
|
||||||
'''A Mastodon client for playing your friends' music.'''
|
'''A Mastodon client that automatically plays your friends' music as they toot links to it.'''
|
||||||
|
|
||||||
from fediplug.cli import cli
|
from fediplug.cli import cli
|
||||||
|
|
106
fediplug/buttplugio.py
Normal file
106
fediplug/buttplugio.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
'''Buttplug controller'''
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from buttplug import Client, WebsocketConnector, ProtocolSpec
|
||||||
|
|
||||||
|
from fediplug.cli import options
|
||||||
|
import fediplug.env as env
|
||||||
|
|
||||||
|
async def connect_plug_client():
|
||||||
|
'''create Client object and connect plug client to Intiface Central or similar'''
|
||||||
|
# And now we're in the main function. First, we'll need to set up a client
|
||||||
|
# object. This is our conduit to the server.
|
||||||
|
# We create a Client object, passing it the name we want for the client.
|
||||||
|
# Names are shown in things like Intiface Central. We also can specify the
|
||||||
|
# protocol version we want to use, which will default to the latest version.
|
||||||
|
plug_client = Client("fediplug", ProtocolSpec.v3)
|
||||||
|
|
||||||
|
# Now we have a client called "Test Client", but it's not connected to
|
||||||
|
# anything yet. We can fix that by creating a connector. Connectors
|
||||||
|
# allow clients to talk to servers through different methods, including:
|
||||||
|
# - Websockets
|
||||||
|
# - IPC (Not currently available in Python)
|
||||||
|
# - WebRTC (Not currently available in Python)
|
||||||
|
# - TCP/UDP (Not currently available in Python)
|
||||||
|
# For now, all we've implemented in python is a Websocket connector, so
|
||||||
|
# we'll use that. This connector will connect to Intiface Central/Engine
|
||||||
|
# on the local machine, using the 12345 port for insecure websockets.
|
||||||
|
# We also pass the client logger so that it is used as the parent.
|
||||||
|
connector = WebsocketConnector("ws://127.0.0.1:12345", logger=plug_client.logger)
|
||||||
|
|
||||||
|
# Finally, we connect.
|
||||||
|
# If this succeeds, we'll be connected. If not, we'll probably have some
|
||||||
|
# sort of exception thrown of type ButtplugError.
|
||||||
|
try:
|
||||||
|
await plug_client.connect(connector)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Could not connect to server, exiting: {e}")
|
||||||
|
return
|
||||||
|
print('plug client connected')
|
||||||
|
return plug_client
|
||||||
|
|
||||||
|
async def scan_devices(plug_client):
|
||||||
|
# Now we move on to looking for devices. We will tell the server to start
|
||||||
|
# scanning for devices. It returns while it is scanning, so we will wait
|
||||||
|
# for 10 seconds, and then we will tell the server to stop scanning.
|
||||||
|
await plug_client.start_scanning()
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
await plug_client.stop_scanning()
|
||||||
|
|
||||||
|
# We can use the found devices as we see fit. The list of devices is
|
||||||
|
# automatically kept up to date by the client:
|
||||||
|
|
||||||
|
# First, we are going to list the found devices.
|
||||||
|
plug_client.logger.info(f"Devices: {plug_client.devices}")
|
||||||
|
|
||||||
|
# If we have any device we can print the list of devices
|
||||||
|
# and access it by its ID: ( this step is done is trigger_actuators() )
|
||||||
|
if len(plug_client.devices) != 0:
|
||||||
|
print(plug_client.devices)
|
||||||
|
print(len(plug_client.devices), "devices found")
|
||||||
|
return plug_client
|
||||||
|
|
||||||
|
|
||||||
|
async def trigger_actuators(plug_client, play_command):
|
||||||
|
# If we have any device we can access it by its ID:
|
||||||
|
device = plug_client.devices[0]
|
||||||
|
# The most common case among devices is that they have some actuators
|
||||||
|
# which accept a scalar value (0.0-1.0) as their command.
|
||||||
|
play_command = (1, 1)
|
||||||
|
if len(device.actuators) != 0:
|
||||||
|
print(len(device.actuators), "actuators found")
|
||||||
|
# cycle through all actuators in device
|
||||||
|
print(device.actuators)
|
||||||
|
for actuator in device.actuators:
|
||||||
|
await actuator.command(play_command[0])
|
||||||
|
print("generic actuator")
|
||||||
|
await asyncio.sleep(play_command[1])
|
||||||
|
#stops all actuators
|
||||||
|
for actuator in device.actuators:
|
||||||
|
await actuator.command(0)
|
||||||
|
'''
|
||||||
|
# Some devices may have linear actuators which need a different command.
|
||||||
|
# The first parameter is the time duration in ms and the second the
|
||||||
|
# position for the linear axis (0.0-1.0).
|
||||||
|
if len(device.linear_actuators) != 0:
|
||||||
|
await device.linear_actuators[0].command(1000, 0.5)
|
||||||
|
print("linear actuator")
|
||||||
|
|
||||||
|
# Other devices may have rotatory actuators which another command.
|
||||||
|
# The first parameter is the speed (0.0-1.0) and the second parameter
|
||||||
|
# is a boolean, true for clockwise, false for anticlockwise.
|
||||||
|
if len(device.rotatory_actuators) != 0:
|
||||||
|
await device.rotatory_actuators[0].command(0.5, True)
|
||||||
|
print("rotary actuator")
|
||||||
|
'''
|
||||||
|
|
||||||
|
async def disconnect_plug_client(plug_client):
|
||||||
|
# Disconnect the plug_client.
|
||||||
|
await plug_client.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
# First things first. We set logging to the console and at INFO level.
|
||||||
|
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
|
@ -6,23 +6,18 @@ import os
|
||||||
path = os.path
|
path = os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import atexit
|
||||||
import appdirs
|
import appdirs
|
||||||
import click
|
import anyio
|
||||||
|
import click as click
|
||||||
import atexit
|
import atexit
|
||||||
from mastodon import Mastodon
|
from mastodon import Mastodon
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from fediplug.dirs import DIRS
|
from fediplug.dirs import DIRS
|
||||||
import fediplug.mastodon as mastodon
|
import fediplug.mastodon as mastodon
|
||||||
import fediplug.keyring as keyring
|
import fediplug.keyring as keyring
|
||||||
|
import fediplug.buttplugio as buttplugio
|
||||||
def ensure_dirs():
|
|
||||||
'''Make sure the application directories exist.'''
|
|
||||||
|
|
||||||
if not path.exists(DIRS.user_config_dir):
|
|
||||||
os.makedirs(DIRS.user_config_dir)
|
|
||||||
|
|
||||||
if not path.exists(DIRS.user_cache_dir):
|
|
||||||
os.makedirs(DIRS.user_cache_dir)
|
|
||||||
|
|
||||||
def get_access_token(instance):
|
def get_access_token(instance):
|
||||||
'''Ensure the user credential exists.'''
|
'''Ensure the user credential exists.'''
|
||||||
|
@ -30,7 +25,7 @@ def get_access_token(instance):
|
||||||
keyring.migrate_access_token(instance)
|
keyring.migrate_access_token(instance)
|
||||||
|
|
||||||
if not keyring.has_credential(instance, keyring.CREDENTIAL_ACCESS_TOKEN):
|
if not keyring.has_credential(instance, keyring.CREDENTIAL_ACCESS_TOKEN):
|
||||||
click.echo('user credential for {} does not exist; try `fediplug login`'.format(instance))
|
click.echo(f'user credential for {instance} does not exist; try `fediplug login`')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
return keyring.get_credential(instance, keyring.CREDENTIAL_ACCESS_TOKEN)
|
return keyring.get_credential(instance, keyring.CREDENTIAL_ACCESS_TOKEN)
|
||||||
|
@ -42,7 +37,7 @@ def get_client_credentials(instance):
|
||||||
|
|
||||||
if not (keyring.has_credential(instance, keyring.CREDENTIAL_CLIENT_ID) and
|
if not (keyring.has_credential(instance, keyring.CREDENTIAL_CLIENT_ID) and
|
||||||
keyring.has_credential(instance, keyring.CREDENTIAL_CLIENT_SECRET)):
|
keyring.has_credential(instance, keyring.CREDENTIAL_CLIENT_SECRET)):
|
||||||
click.echo('client credentials for {} do not exist; try `fediplug register`'.format(instance))
|
click.echo(f'client credentials for {instance} do not exist; try `fediplug register`')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -57,8 +52,6 @@ def cli(debug):
|
||||||
|
|
||||||
options['debug'] = debug
|
options['debug'] = debug
|
||||||
|
|
||||||
ensure_dirs()
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.argument('instance')
|
@click.argument('instance')
|
||||||
def register(instance):
|
def register(instance):
|
||||||
|
@ -85,27 +78,13 @@ def login(instance):
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.argument('instance')
|
@click.argument('instance')
|
||||||
@click.argument('users', nargs=-1)
|
@click.argument('users', nargs=-1)
|
||||||
@click.option('--clean-up-files', is_flag=True)
|
def stream(instance, users):
|
||||||
def stream(instance, users, clean_up_files):
|
'''control buttplug.io device from your timeline.'''
|
||||||
'''Stream music from your timeline.'''
|
event_loop = asyncio.get_event_loop()
|
||||||
if ( clean_up_files ):
|
plug_client = event_loop.run_until_complete(buttplugio.connect_plug_client())
|
||||||
atexit.register(delete_files)
|
plug_client = event_loop.run_until_complete(buttplugio.scan_devices(plug_client))
|
||||||
|
|
||||||
client_id, client_secret = get_client_credentials(instance)
|
client_id, client_secret = get_client_credentials(instance)
|
||||||
access_token = get_access_token(instance)
|
access_token = get_access_token(instance)
|
||||||
|
|
||||||
mastodon.stream(instance, users, client_id, client_secret, access_token, cache_dir=DIRS.user_cache_dir)
|
mastodon.stream(instance, users, client_id, client_secret, access_token, plug_client, event_loop)
|
||||||
|
|
||||||
def delete_files():
|
|
||||||
cache_dir = DIRS.user_cache_dir
|
|
||||||
for the_file in os.listdir(cache_dir):
|
|
||||||
file_path = os.path.join(cache_dir, the_file)
|
|
||||||
if os.path.isfile(file_path):
|
|
||||||
os.remove(file_path)
|
|
||||||
print('deleted ' + the_file)
|
|
||||||
|
|
||||||
@cli.command()
|
|
||||||
def clean_up_files():
|
|
||||||
delete_files()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,15 @@ LISTEN_TO_HASHTAG = 'fediplug'
|
||||||
from os import umask
|
from os import umask
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from lxml.etree import HTML # pylint: disable=no-name-in-module
|
import lxml.html as lh
|
||||||
|
from lxml.html.clean import clean_html
|
||||||
import mastodon
|
import mastodon
|
||||||
from youtube_dl.utils import DownloadError
|
import asyncio
|
||||||
|
|
||||||
from fediplug.cli import options
|
from fediplug.cli import options
|
||||||
import fediplug.keyring as keyring
|
import fediplug.keyring as keyring
|
||||||
from fediplug.queue import Queue
|
#from fediplug.queue import Queue
|
||||||
|
from fediplug.buttplugio import trigger_actuators
|
||||||
|
|
||||||
Mastodon = mastodon.Mastodon
|
Mastodon = mastodon.Mastodon
|
||||||
|
|
||||||
|
@ -22,19 +24,20 @@ def api_base_url(instance):
|
||||||
return 'https://' + instance
|
return 'https://' + instance
|
||||||
|
|
||||||
class StreamListener(mastodon.StreamListener):
|
class StreamListener(mastodon.StreamListener):
|
||||||
'''Listens to a Mastodon timeline and adds links the given Queue.'''
|
'''Listens to a Mastodon timeline and adds buttplug instructions the given queue.'''
|
||||||
|
|
||||||
def __init__(self, queue, instance, users):
|
def __init__(self, plug_client, instance, users, event_loop):
|
||||||
self.queue = queue
|
self.plug_client = plug_client
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.users = users
|
self.users = users
|
||||||
|
self.event_loop = event_loop
|
||||||
|
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print('listener initialized with users={!r}'.format(self.users))
|
print(rf'listener initialized with users={self.users}')
|
||||||
|
|
||||||
def on_update(self, status):
|
def on_update(self, status):
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print('incoming status: acct={!r}'.format(status.account.acct))
|
print(rf'incoming status: acct={status.account.acct}')
|
||||||
|
|
||||||
if self.users and normalize_username(status.account.acct, self.instance) not in self.users:
|
if self.users and normalize_username(status.account.acct, self.instance) not in self.users:
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
|
@ -43,20 +46,29 @@ class StreamListener(mastodon.StreamListener):
|
||||||
|
|
||||||
tags = extract_tags(status)
|
tags = extract_tags(status)
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print('expecting: {!r}, extracted tags: {!r}'.format(LISTEN_TO_HASHTAG, tags))
|
print(rf'expecting: {LISTEN_TO_HASHTAG}, extracted tags: {tags}')
|
||||||
|
|
||||||
if LISTEN_TO_HASHTAG in tags:
|
if LISTEN_TO_HASHTAG in tags:
|
||||||
links = extract_links(status)
|
''' Here we extract the instructions for the butplug'''
|
||||||
|
# TODO: still need to write extraction code
|
||||||
|
buttplug_instructions = extract_buttplug_instructions(status)
|
||||||
|
click.echo('queueing instructions')
|
||||||
|
self.event_loop.run_until_complete(trigger_actuators(self.plug_client, buttplug_instructions))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print('links: {!r}'.format(links))
|
print(rf'instructions: {buttplug_instructions}')
|
||||||
|
|
||||||
for link in links:
|
for link in links:
|
||||||
try:
|
try:
|
||||||
click.echo('==> Trying {}'.format(link))
|
click.echo(rf'==> Trying {link}')
|
||||||
self.queue.add(link)
|
self.queue.add(link)
|
||||||
return
|
return
|
||||||
except DownloadError:
|
except DownloadError:
|
||||||
pass
|
pass
|
||||||
|
'''
|
||||||
|
|
||||||
def register(instance):
|
def register(instance):
|
||||||
'''Register fediplug to a Mastodon server and save the client credentials.'''
|
'''Register fediplug to a Mastodon server and save the client credentials.'''
|
||||||
|
@ -83,22 +95,23 @@ def login(instance, client_id, client_secret, grant_code):
|
||||||
access_token = client.log_in(code=grant_code, scopes=['read'])
|
access_token = client.log_in(code=grant_code, scopes=['read'])
|
||||||
keyring.set_credential(instance, keyring.CREDENTIAL_ACCESS_TOKEN, access_token)
|
keyring.set_credential(instance, keyring.CREDENTIAL_ACCESS_TOKEN, access_token)
|
||||||
|
|
||||||
def stream(instance, users, client_id, client_secret, access_token, cache_dir='.'):
|
def stream(instance, users, client_id, client_secret, access_token, plug_client, event_loop):
|
||||||
'''Stream statuses and add them to a queue.'''
|
'''Stream statuses and add them to a queue.'''
|
||||||
|
|
||||||
client = build_client(instance, client_id, client_secret, access_token)
|
client = build_client(instance, client_id, client_secret, access_token)
|
||||||
users = [normalize_username(user, instance) for user in users]
|
users = [normalize_username(user, instance) for user in users]
|
||||||
listener = StreamListener(Queue(cache_dir), instance, users)
|
listener = StreamListener(plug_client, instance, users, event_loop)
|
||||||
|
|
||||||
|
|
||||||
existing_statuses = client.timeline_hashtag(LISTEN_TO_HASHTAG, limit=1)
|
existing_statuses = client.timeline_hashtag(LISTEN_TO_HASHTAG, limit=1)
|
||||||
|
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print('existing_statuses: {!r}'.format(existing_statuses))
|
print(rf'existing_statuses: {existing_statuses}')
|
||||||
|
|
||||||
for status in existing_statuses:
|
for status in existing_statuses:
|
||||||
listener.on_update(status)
|
listener.on_update(status)
|
||||||
|
|
||||||
click.echo('==> Streaming from {}'.format(instance))
|
click.echo(f'==> Streaming from {instance}')
|
||||||
client.stream_user(listener)
|
client.stream_user(listener)
|
||||||
|
|
||||||
def extract_tags(toot):
|
def extract_tags(toot):
|
||||||
|
@ -110,29 +123,18 @@ def normalize_username(user, instance):
|
||||||
user = user.lstrip('@')
|
user = user.lstrip('@')
|
||||||
parts = user.split('@')
|
parts = user.split('@')
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print('parts: {!r}'.format(parts))
|
print(rf'parts: {parts}')
|
||||||
|
|
||||||
if len(parts) == 1 or parts[1] == instance:
|
if len(parts) == 1 or parts[1] == instance:
|
||||||
return parts[0]
|
return parts[0]
|
||||||
else:
|
else:
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def link_is_internal(link):
|
def extract_buttplug_instructions(toot):
|
||||||
'''Determines if a link is internal to the Mastodon instance.'''
|
'''Extract buttplug instruction informations from a toot.'''
|
||||||
|
doc_list = []
|
||||||
classes = link.attrib.get('class', '').split(' ')
|
doc = lh.fromstring(toot['content'])
|
||||||
|
doc = clean_html(doc)
|
||||||
if options['debug']:
|
doc_list.append(doc.text_content())
|
||||||
print('href: {!r}, classes: {!r}'.format(link.attrib['href'], classes))
|
print(rf'extracted buttplug_instruction: {doc_list}')
|
||||||
|
return doc_list
|
||||||
if classes:
|
|
||||||
return 'mention' in classes
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def extract_links(toot):
|
|
||||||
'''Extract all external links from a toot.'''
|
|
||||||
|
|
||||||
html = HTML(toot['content'])
|
|
||||||
all_links = html.cssselect('a')
|
|
||||||
return [link.attrib['href'] for link in all_links if not link_is_internal(link)]
|
|
|
@ -2,118 +2,73 @@
|
||||||
|
|
||||||
from os import path, listdir, makedirs, remove, utime
|
from os import path, listdir, makedirs, remove, utime
|
||||||
from time import time, localtime
|
from time import time, localtime
|
||||||
from subprocess import Popen, run
|
|
||||||
from threading import Thread, Lock
|
from threading import Thread, Lock
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from youtube_dl import YoutubeDL, utils
|
import asyncio
|
||||||
|
|
||||||
from fediplug.cli import options
|
from fediplug.cli import options
|
||||||
import fediplug.env as env
|
import fediplug.env as env
|
||||||
|
import fediplug.buttplugio as buttplugio
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''--deprecated--'''
|
||||||
class Queue(object):
|
class Queue(object):
|
||||||
'''The play queue.'''
|
'''The play queue.'''
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
def __init__(self, cache_dir):
|
def __init__(self, plug_client):
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
self.playing = False
|
self.playing = False
|
||||||
self.queue = []
|
self.queue = []
|
||||||
self.cache_dir = cache_dir
|
self.plug_client = plug_client
|
||||||
|
|
||||||
def add(self, url):
|
def add(self, buttplug_instructions):
|
||||||
'''Fetches the url and adds the resulting audio to the play queue.'''
|
'''adds the buttplug instructions to the play queue.'''
|
||||||
|
|
||||||
filenames = Getter(self.cache_dir).get(url)
|
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.queue.extend(filenames)
|
self.queue.extend(buttplug_instructions)
|
||||||
if not self.playing:
|
if not self.playing:
|
||||||
self._play(self.queue.pop(0), self._play_finished)
|
self._play(self.queue.pop(0), self._play_finished)
|
||||||
|
|
||||||
def _play(self, filename, cb_complete):
|
def _play(self, buttplug_instructions, cb_complete):
|
||||||
self.playing = True
|
self.playing = True
|
||||||
|
|
||||||
def _run_thread(filename, cb_complete):
|
def _run_thread(buttplug_instructions, cb_complete):
|
||||||
play_command = build_play_command(filename)
|
play_command = build_play_command(buttplug_instructions)
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
click.echo(f'==> Playing {filename} with {play_command}')
|
click.echo(f'==> Playing {buttplug_instructions} with {play_command}')
|
||||||
else:
|
else:
|
||||||
click.echo(f'==> Playing {filename}')
|
click.echo(f'==> Playing {buttplug_instructions}')
|
||||||
run(play_command)
|
"""
|
||||||
|
loop = asyncio.new_event_loop
|
||||||
|
asyncio.run_coroutine_threadsafe(buttplugio.trigger_actuators(self.plug_client, play_command), loop)
|
||||||
|
# run command THIS DOES NOT WORK RIGHT NOW ##### THIS REALLY NEEDS TO BE FIXED
|
||||||
|
#loop = asyncio.events._get_running_loop()
|
||||||
|
#print(loop) """
|
||||||
|
|
||||||
|
print('foo')
|
||||||
|
|
||||||
click.echo('==> Playback complete')
|
click.echo('==> Playback complete')
|
||||||
cb_complete()
|
cb_complete()
|
||||||
|
|
||||||
thread = Thread(target=_run_thread, args=(filename, cb_complete))
|
thread = Thread(target=_run_thread, args=(buttplug_instructions, cb_complete))
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
def _play_finished(self):
|
def _play_finished(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.playing = False
|
self.playing = False
|
||||||
if self.queue:
|
if self.queue:
|
||||||
self._play(self.queue.pop(0), self._play_finished)
|
self._play(self.queue.pop(0), self._play_finished)
|
||||||
|
|
||||||
class Getter(object):
|
|
||||||
'''Fetches music from a url.'''
|
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
def build_play_command(buttplug_instructions):
|
||||||
def __init__(self, cache_dir):
|
|
||||||
self.filename = None
|
|
||||||
self.filenames = []
|
|
||||||
self.cache_dir = cache_dir
|
|
||||||
|
|
||||||
def _progress_hook(self, progress):
|
|
||||||
if options['debug']:
|
|
||||||
print('progress hook: status {!r}, filename {!r}'.format(progress['status'], progress['filename']))
|
|
||||||
|
|
||||||
if (progress['status'] in ('downloading', 'finished') and
|
|
||||||
progress['filename'] not in self.filenames):
|
|
||||||
self.filenames.append(progress['filename'])
|
|
||||||
|
|
||||||
def get(self, url):
|
|
||||||
'''Fetches music from the given url.'''
|
|
||||||
|
|
||||||
'''deleting files here'''
|
|
||||||
auto_delete_files(self.cache_dir)
|
|
||||||
|
|
||||||
ytdl_options = {
|
|
||||||
'format': 'mp3/mp4',
|
|
||||||
'nocheckcertificate': env.no_check_certificate(),
|
|
||||||
'outtmpl': path.join(self.cache_dir, utils.DEFAULT_OUTTMPL),
|
|
||||||
'progress_hooks': [self._progress_hook],
|
|
||||||
'restrictfilenames': True,
|
|
||||||
'quiet': True,
|
|
||||||
'no_warnings': True
|
|
||||||
}
|
|
||||||
|
|
||||||
if options['debug']:
|
|
||||||
ytdl_options['quiet'] = False
|
|
||||||
ytdl_options['no_warnings'] = False
|
|
||||||
|
|
||||||
with YoutubeDL(ytdl_options) as downloader:
|
|
||||||
downloader.download([url])
|
|
||||||
|
|
||||||
for file in self.filenames:
|
|
||||||
utime(file)
|
|
||||||
|
|
||||||
return self.filenames
|
|
||||||
|
|
||||||
def build_play_command(filename):
|
|
||||||
'''Builds a play command for the given filename.'''
|
'''Builds a play command for the given filename.'''
|
||||||
|
# hardcoded for now
|
||||||
|
# return tuple with ( strength [0.0-1.0] , duration [in s] )
|
||||||
|
|
||||||
filename = rf"{filename}"
|
return (0.5, 1)
|
||||||
|
|
||||||
command_args = env.play_command().split()
|
|
||||||
command_args.append(filename)
|
|
||||||
return command_args
|
|
||||||
|
|
||||||
def auto_delete_files(cache_dir):
|
|
||||||
for the_file in listdir(cache_dir):
|
|
||||||
file_path = path.join(cache_dir, the_file)
|
|
||||||
if path.isfile(file_path):
|
|
||||||
file_time = path.getmtime(file_path)
|
|
||||||
if file_time + 604800 < time():
|
|
||||||
remove(file_path)
|
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -11,7 +11,12 @@ setup(
|
||||||
'lxml',
|
'lxml',
|
||||||
'Mastodon.py',
|
'Mastodon.py',
|
||||||
'python-dotenv',
|
'python-dotenv',
|
||||||
'youtube-dl'
|
'asyncio',
|
||||||
|
'asyncclick',
|
||||||
|
'anyio',
|
||||||
|
'keyring',
|
||||||
|
'buttplug-py'
|
||||||
|
|
||||||
],
|
],
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
|
|
Loading…
Reference in a new issue