playlistalo.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. #!/usr/bin/env python3
  2. #Playlistalo - simpatico script che legge le cartelle e genera la playlist
  3. #Requirements: youtube_dl Mastodon.py python-telegram-bot validators python-musicpd
  4. #import youtube_dl
  5. import yt_dlp as youtube_dl
  6. import shutil
  7. import sys
  8. import re
  9. import os
  10. import validators
  11. from glob import glob
  12. import json
  13. import time
  14. import subprocess
  15. import random
  16. import configparser
  17. import hashlib
  18. from urllib.parse import urlparse, parse_qs
  19. import argparse
  20. from musicpd import MPDClient
  21. from telegram import Update
  22. from telegram.ext import Application, ContextTypes, MessageHandler, filters
  23. from mastodon import Mastodon, StreamListener
  24. scriptpath = os.path.dirname(os.path.realpath(__file__))
  25. #Crea le cartelle
  26. os.makedirs("playlist", exist_ok=True)
  27. os.makedirs("fallback", exist_ok=True)
  28. SHUFFLEUSERS = False
  29. SHUFFLESONGS = False
  30. SHUFFLEFALLBACK = False
  31. ARCHIVE = True
  32. TELEGRAM_TOKEN = ""
  33. MASTODON_TOKEN = ""
  34. MASTODON_URL = ""
  35. ANNOUNCEREPEAT = 5
  36. #Scrivi la prima configurazione
  37. configfile = 'playlistalo.conf'
  38. if not os.path.exists(configfile):
  39. config = configparser.ConfigParser()
  40. config['playlistalo'] = {'ShuffleUsers': SHUFFLEUSERS, 'ShuffleSongs': SHUFFLESONGS, 'ShuffleFallback': SHUFFLEFALLBACK, 'Archive': ARCHIVE, 'Telegram_token': TELEGRAM_TOKEN, 'Mastodon_token': MASTODON_TOKEN, 'Mastodon_url': MASTODON_URL}
  41. with open(configfile, 'w') as f:
  42. config.write(f)
  43. #Leggi la configurazione
  44. config = configparser.ConfigParser()
  45. config.read(configfile)
  46. playlistaloconf = config['playlistalo']
  47. SHUFFLEUSERS = playlistaloconf.getboolean('ShuffleUsers', SHUFFLEUSERS)
  48. SHUFFLESONGS = playlistaloconf.getboolean('ShuffleSongs', SHUFFLESONGS)
  49. SHUFFLEFALLBACK = playlistaloconf.getboolean('ShuffleFallback', SHUFFLEFALLBACK)
  50. ARCHIVE = playlistaloconf.getboolean('Archive', ARCHIVE)
  51. TELEGRAM_TOKEN = playlistaloconf.get('Telegram_token', TELEGRAM_TOKEN)
  52. MASTODON_TOKEN = playlistaloconf.get('Mastodon_token',MASTODON_TOKEN)
  53. MASTODON_URL = playlistaloconf.get('Mastodon_url', MASTODON_URL)
  54. def video_id(value):
  55. """
  56. Examples:
  57. - http://youtu.be/SA2iWivDJiE
  58. - http://www.youtube.com/watch?v=_oPAwA_Udwc&feature=feedu
  59. - http://www.youtube.com/embed/SA2iWivDJiE
  60. - http://www.youtube.com/v/SA2iWivDJiE?version=3&hl=en_US
  61. """
  62. query = urlparse(value)
  63. if query.hostname == 'youtu.be':
  64. return query.path[1:]
  65. if query.hostname in ('www.youtube.com', 'm.youtube.com', 'youtube.com'):
  66. if query.path == '/watch':
  67. p = parse_qs(query.query)
  68. return p['v'][0]
  69. if query.path[:7] == '/embed/':
  70. return query.path.split('/')[2]
  71. if query.path[:3] == '/v/':
  72. return query.path.split('/')[2]
  73. # fail?
  74. return None
  75. def addurl(url, user = "-unknown-"):
  76. #print ('--- Inizio ---')
  77. os.makedirs("cache", exist_ok=True)
  78. ydl_opts = {
  79. 'format': 'bestaudio[ext=m4a]',
  80. 'outtmpl': 'cache/%(id)s.m4a',
  81. 'no-cache-dir': True,
  82. 'noplaylist': True,
  83. 'quiet': True,
  84. }
  85. url = url.strip()
  86. print ("url: " + url)
  87. print ("user: " + user)
  88. if not validators.url(url):
  89. print ('--- URL malformato ---')
  90. return ("Err: url non valido")
  91. #trova l'id dall'url
  92. id = video_id(url)
  93. if id:
  94. #cerca se ho gia' il json e il file
  95. if not (glob(os.path.join("cache", id + ".json")) and glob(os.path.join("cache", id + ".m4a"))):
  96. id = None
  97. else:
  98. #legge le info
  99. with open(os.path.join("cache", id + ".json")) as infile:
  100. j = json.load(infile)
  101. title = normalizetext(j.get('title'))
  102. track = normalizetext(j.get('track'))
  103. artist = normalizetext(j.get('artist'))
  104. album = normalizetext(j.get('album'))
  105. filetemp = os.path.join("cache", id + ".m4a")
  106. print ('id: %s' %(id))
  107. print ('title: %s' %(title))
  108. if not id:
  109. with youtube_dl.YoutubeDL(ydl_opts) as ydl:
  110. try:
  111. meta = ydl.extract_info(url, download = False)
  112. except youtube_dl.DownloadError as detail:
  113. print ('--- Errore video non disponibile ---')
  114. print(str(detail))
  115. return ("Err: " + str(detail))
  116. id = meta.get('id').strip()
  117. title = normalizetext(meta.get('title'))
  118. track = normalizetext(meta.get('track'))
  119. artist = normalizetext(meta.get('artist'))
  120. album = normalizetext(meta.get('album'))
  121. print ('id: %s' %(id))
  122. print ('title: %s' %(title))
  123. #scrivo il json
  124. with open(os.path.join("cache", id + ".json"), 'w') as outfile:
  125. json.dump(meta, outfile, indent=4)
  126. #ho letto le info, ora controllo se il file esiste altrimenti lo scarico
  127. #miglioria: controllare se upload_date e' uguale a quella del json gia' esistente
  128. filetemp = os.path.join("cache", id + ".m4a")
  129. if not glob(filetemp):
  130. print ('--- Scarico ---')
  131. ydl.download([url]) #non ho capito perche' ma senza [] fa un carattere per volta
  132. if not os.path.isfile(filetemp):
  133. return("Err: file non scaricato")
  134. return(add(filetemp, user, title, id, track, artist, album))
  135. def md5(fname):
  136. hash_md5 = hashlib.md5()
  137. with open(fname, "rb") as f:
  138. for chunk in iter(lambda: f.read(4096), b""):
  139. hash_md5.update(chunk)
  140. return hash_md5.hexdigest()
  141. def add(filetemp, user = "-unknown-", title = None, id = None, track = None, artist = None, album = None):
  142. if not id:
  143. id = md5(filetemp)
  144. if not title:
  145. title = os.path.splitext(os.path.basename(filetemp))[0]
  146. if not track:
  147. track = ''
  148. if not artist:
  149. artist = ''
  150. if not album:
  151. album = ''
  152. #se il file esiste gia' in playlist salto (potrebbe esserci, anche rinominato)
  153. if glob("playlist/**/*|" + id + ".*"):
  154. print ('--- File già presente ---')
  155. return ("Err: %s [%s] già presente" %(title, id))
  156. os.makedirs("playlist/" + user, exist_ok=True)
  157. #qui compone il nome del file
  158. fnam = (artist + " - " + track).strip() + "|" + title + "|" + id + ".m4a"
  159. if SHUFFLESONGS:
  160. fileout = str(random.randrange(10**6)).zfill(14) + "|" + fnam
  161. else:
  162. fileout = time.strftime("%Y%m%d%H%M%S") + "|" + fnam
  163. fileout = os.path.join("playlist/" + user, fileout)
  164. #se il file esiste gia' in archive prendo quello
  165. if glob("archive/*|" + id + ".*"):
  166. print ('--- File in archivio ---')
  167. copyfile(glob("archive/*|" + id + ".*")[0], fileout)
  168. else:
  169. print ('--- Converto ---')
  170. print (fileout)
  171. trimaudio(filetemp, fileout)
  172. print ('--- Fine ---')
  173. if not os.path.isfile(fileout):
  174. return("Err: file non convertito")
  175. if ARCHIVE:
  176. os.makedirs("archive", exist_ok=True)
  177. if not glob("archive/*|" + id + ".*"):
  178. shutil.copy2(fileout, "archive")
  179. #cerca la posizione del pezzo appena inserito
  180. pos = getposition(fileout)
  181. return ("OK: %s [%s] aggiunto alla playlist in posizione #%s" %(title, id, pos))
  182. def normalizetext(s):
  183. if s is None:
  184. return None
  185. else:
  186. s = re.sub(r'[\\|/|:|*|?|"|<|>|\|]',r'',s)
  187. s = " ".join(s.split())
  188. return s
  189. def listplaylist():
  190. pl = []
  191. pl2 = []
  192. for udir in sorted(glob("playlist/*/")):
  193. #print (udir)
  194. user = os.path.basename(os.path.dirname(udir))
  195. #cerca il file last
  196. last = ""
  197. if os.path.exists(udir + "/last"):
  198. f = open(udir + "/last", "r")
  199. last = f.readline().rstrip()
  200. else:
  201. files = [x for x in sorted(glob(udir + "/*")) if not os.path.basename(x) == "last"]
  202. if files:
  203. last = os.path.basename(files[0]).split("|")[0]
  204. #print ("LAST: " + last)
  205. #leggi i file nella cartella
  206. files = [x for x in sorted(glob(udir + "/*")) if not os.path.basename(x) == "last"]
  207. seq = 0
  208. for file in files:
  209. bn = os.path.splitext(os.path.basename(file))[0]
  210. #print ("BASENAME: " + bn)
  211. seq = seq + 1
  212. dat = bn.split("|")[0]
  213. if len(bn.split("|")) < 4:
  214. nam = bn.split("|")[1]
  215. cod = bn.split("|")[2]
  216. else:
  217. nam = bn.split("|")[2]
  218. cod = bn.split("|")[3]
  219. key = "-".join([str(seq).zfill(5), last, dat])
  220. #print ("KEY: " + key)
  221. plsong = [key, file, user, nam, cod]
  222. pl.append(plsong)
  223. pl.sort()
  224. #rimuove la prima colonna, che serve solo per l'ordinamento
  225. pl2 = [x[1:] for x in pl]
  226. #print (pl)
  227. #print ('\n'.join([", ".join(x) for x in pl]))
  228. #print ('\n'.join([x[0] for x in pl]))
  229. return pl2
  230. def listfallback():
  231. pl = []
  232. pl2 = []
  233. #leggi i file nella cartella
  234. files = [x for x in sorted(glob("fallback/*")) if not os.path.basename(x) == "last"]
  235. seq = 0
  236. for file in files:
  237. bn = os.path.splitext(os.path.basename(file))[0]
  238. seq = seq + 1
  239. dat = bn.split("|")[0]
  240. if len(bn.split("|")) < 4:
  241. nam = bn.split("|")[1]
  242. cod = bn.split("|")[2]
  243. else:
  244. nam = bn.split("|")[2]
  245. cod = bn.split("|")[3]
  246. key = "-".join([str(seq).zfill(5), dat])
  247. plsong = [key, file, "fallback", nam, cod]
  248. pl.append(plsong)
  249. pl.sort()
  250. #rimuove la prima colonna, che serve solo per l'ordinamento
  251. pl2 = [x[1:] for x in pl]
  252. return pl2
  253. def playlocal():
  254. if SHUFFLEUSERS:
  255. shuffleusers()
  256. if SHUFFLEFALLBACK:
  257. shufflefallback()
  258. while True:
  259. plt = listtot(1)
  260. if plt:
  261. song = plt[0][0]
  262. print(song)
  263. #qui fa play
  264. subprocess.call(["mplayer", "-nolirc", "-msglevel", "all=0:statusline=5", song])
  265. consume(song)
  266. def clean():
  267. #cancella tutto dalla playlist
  268. shutil.rmtree("playlist")
  269. os.makedirs("playlist")
  270. def shuffleusers():
  271. #scrivere un numero casuale dentro a tutti i file last
  272. for udir in sorted(glob("playlist/*/")):
  273. #print (udir)
  274. with open(udir + "/last", "w") as f:
  275. f.write(str(random.randrange(10**6)).zfill(14))
  276. def shufflefallback():
  277. #rinominare con un numero casuale i file in fallback
  278. files = [x for x in glob("fallback/*") if not os.path.basename(x) == "last"]
  279. for file in files:
  280. fname = str(random.randrange(10**6)).zfill(14) + "|" + "|".join(os.path.basename(file).split("|")[1:])
  281. fname = os.path.dirname(file) + "/" + fname
  282. os.rename(file, fname)
  283. def getposition(file):
  284. pl = listplaylist()
  285. try:
  286. return([x[0] for x in pl].index(file) + 1)
  287. except:
  288. pass
  289. async def telegram_msg_parser(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
  290. print("Messaggio ricevuto")
  291. msg = update.message.text
  292. id = str(update.message.from_user.id)
  293. username = update.message.from_user.username
  294. urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', msg)
  295. user = "t_" + "-".join([i for i in [id, username] if i])
  296. #print (urls)
  297. #print (user)
  298. if not urls:
  299. await update.message.reply_text("Non ho trovato indirizzi validi...")
  300. return()
  301. await update.message.reply_text("Messaggio ricevuto. Elaboro...")
  302. for url in urls:
  303. #update.message.reply_text("Scarico %s" %(url))
  304. # start the download
  305. dl = addurl(url, user)
  306. await update.message.reply_text(dl)
  307. def telegram_bot():
  308. print ("Bot avviato")
  309. application = Application.builder().token(TELEGRAM_TOKEN).build()
  310. # on non command i.e message - echo the message on Telegram
  311. application.add_handler(MessageHandler(filters.TEXT, telegram_msg_parser))
  312. # Run the bot until the user presses Ctrl-C
  313. application.run_polling(allowed_updates=Update.ALL_TYPES)
  314. class MastodonListener(StreamListener):
  315. # andiamo a definire il metodo __init__, che prende una istanza di Mastodon come parametro opzionale e lo setta nella prop. self.mastodon
  316. def __init__(self, mastodonInstance=None):
  317. self.mastodon = mastodonInstance
  318. def on_notification(self, notification):
  319. print("Messaggio ricevuto")
  320. #try:
  321. msg = notification["status"]["content"]
  322. id = str(notification["account"]["id"])
  323. username = notification["account"]["acct"]
  324. #except KeyError:
  325. # return
  326. msg = msg.replace("<br>", "\n")
  327. msg = re.compile(r'<.*?>').sub('', msg)
  328. urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', msg)
  329. user = "m_" + "-".join([i for i in [id, username] if i])
  330. #print (urls)
  331. #print (user)
  332. #visibility = notification['status']['visibility']
  333. statusid = notification['status']['id']
  334. if not urls:
  335. self.mastodon.status_post("@" + username + " " + "Non ho trovato indirizzi validi...", in_reply_to_id = statusid, visibility="direct")
  336. return()
  337. self.mastodon.status_post("@" + username + " " + "Messaggio ricevuto. Elaboro...", in_reply_to_id = statusid, visibility="direct")
  338. for url in urls:
  339. # start the download
  340. dl = addurl(url, user)
  341. self.mastodon.status_post("@" + username + " " + dl, in_reply_to_id = statusid, visibility="direct")
  342. def mastodon_bot():
  343. print ("Bot avviato")
  344. mastodon = Mastodon(access_token = MASTODON_TOKEN, api_base_url = MASTODON_URL)
  345. listener = MastodonListener(mastodon)
  346. mastodon.stream_user(listener)
  347. def listtot(res = sys.maxsize):
  348. plt = listplaylist()
  349. if plt:
  350. announcepos = 0
  351. else:
  352. announcepos = getlastannounce()
  353. if len(plt) < res:
  354. for x in listfallback():
  355. if announcepos == 0:
  356. if os.path.exists("announce/repeat.mp3"):
  357. plt.append(["announce/repeat.mp3"])
  358. announcepos = ANNOUNCEREPEAT - 1
  359. else:
  360. announcepos = (announcepos - 1)
  361. plt.append(x)
  362. return plt[:res]
  363. else:
  364. return plt
  365. def getlastannounce():
  366. announcepos = ANNOUNCEREPEAT - 1
  367. try:
  368. with open("announce/last","r") as f:
  369. announcepos=int(f.readline().rstrip())
  370. except:
  371. pass
  372. return announcepos
  373. def setlastannounce(announcepos):
  374. with open("announce/last","w") as f:
  375. f.write(str(announcepos))
  376. def consume(song):
  377. if os.path.exists(song):
  378. print ("Consumo la canzone " + song)
  379. if song.split("/")[0] == "playlist":
  380. os.remove(song)
  381. if not [x for x in glob(os.path.dirname(song) + "/*") if not os.path.basename(x) == "last"]:
  382. shutil.rmtree(os.path.dirname(song))
  383. else:
  384. with open(os.path.dirname(song) + "/last", "w") as f:
  385. f.write(time.strftime("%Y%m%d%H%M%S"))
  386. #resetta il contatore announcelast
  387. setlastannounce(0)
  388. elif song.split("/")[0] == "fallback":
  389. fname = time.strftime("%Y%m%d%H%M%S") + "|" + "|".join(os.path.basename(song).split("|")[1:])
  390. fname = os.path.dirname(song) + "/" + fname
  391. os.rename(song, fname)
  392. announcepos = getlastannounce()
  393. print("Annuncio da " + str(announcepos) + " a " + str(announcepos - 1))
  394. setlastannounce(announcepos - 1)
  395. elif song.split("/")[0] == "announce":
  396. setlastannounce(ANNOUNCEREPEAT)
  397. def addstartannounce():
  398. #aggiunge l'annuncio iniziale
  399. if os.path.exists("announce/start.mp3"):
  400. fileout = "playlist/announce/00000000000000|start|start.mp3"
  401. copyfile("announce/start.mp3", fileout)
  402. def copyfile(source, dest):
  403. if not os.path.exists(dest):
  404. os.makedirs(os.path.dirname(dest), exist_ok=True)
  405. shutil.copy2(source, dest)
  406. def plaympd():
  407. client = MPDClient()
  408. client.timeout = 10 # network timeout in seconds (floats allowed), default: None
  409. client.idletimeout = None # timeout for fetching the result of the idle command is handled seperately, default: None
  410. client.connect("localhost", 6600) # connect to localhost:6600
  411. #print(client.mpd_version)
  412. looptime = 10
  413. synctime = 5
  414. listlen = 10
  415. if client.status()['state'] != "play":
  416. if SHUFFLEUSERS:
  417. shuffleusers()
  418. if SHUFFLEFALLBACK:
  419. shufflefallback()
  420. #stoppa e svuota
  421. client.stop()
  422. client.clear()
  423. #cancella la cartella mpd
  424. if os.path.exists("mpd"):
  425. shutil.rmtree("mpd")
  426. os.makedirs("mpd")
  427. client.update()
  428. while 'updating_db' in client.status():
  429. pass
  430. #aggiunge l'annuncio iniziale
  431. #addstartannounce()
  432. #resetta il lastannounce
  433. setlastannounce(ANNOUNCEREPEAT - 1)
  434. #riempe la playlist
  435. plt = listtot(listlen)
  436. for f in plt:
  437. print(f[0])
  438. copyfile(f[0], "mpd/" + f[0])
  439. client.update()
  440. while 'updating_db' in client.status():
  441. pass
  442. for f in plt:
  443. client.add(f[0])
  444. #consuma il primo e fa play
  445. consume(plt[0][0])
  446. #mpdadd(client, listlen)
  447. client.play(0)
  448. while True:
  449. # print("Current")
  450. # print(client.currentsong())
  451. # print()
  452. # print("Status")
  453. # print(client.status())
  454. # print()
  455. # print("playlist")
  456. # print(client.playlistinfo())
  457. # print()
  458. #controlla se il pezzo e' il primo e consuma le precedenti
  459. status = client.status()
  460. if int(status['song']) > 0:
  461. #consuma la canzone attuale
  462. song = client.playlistinfo()[int(status['song'])]['file']
  463. consume(song)
  464. mpdclean(client)
  465. # if len(client.playlistinfo()) < listlen:
  466. # mpdsync(client, listlen)
  467. # status = client.status()
  468. #controlla se mancano meno di 15 secondi
  469. #timeleft = float(status['duration']) - float(status['elapsed']) #new mpd
  470. timeleft = float(client.currentsong()['time']) - float(status['elapsed']) #old mpd
  471. if timeleft <= looptime + synctime:
  472. time.sleep(max(timeleft - synctime, 0))
  473. print ("Mancano %d secondi" % (synctime))
  474. mpdclean(client)
  475. mpdadd(client, listlen)
  476. time.sleep(looptime)
  477. def mpdclean(client):
  478. #cancella le precedenti
  479. for x in range(int(client.status()['song'])):
  480. song = client.playlistinfo()[0]['file']
  481. consume(song)
  482. client.delete(0)
  483. #e pulisce anche in mpd
  484. if os.path.exists("mpd/" + song):
  485. os.remove("mpd/" + song)
  486. #se non ci sono + file cancella la cartella
  487. if not glob(os.path.dirname("mpd/" + song) + "/*"):
  488. shutil.rmtree(os.path.dirname("mpd/" + song))
  489. def mpdadd(client, listlen):
  490. print("Rigenero la playlist")
  491. plt = listtot(listlen)
  492. #copia i file
  493. for f in plt:
  494. #print(f[0])
  495. copyfile(f[0], "mpd/" + f[0])
  496. client.update()
  497. while 'updating_db' in client.status():
  498. pass
  499. # #cancella tutto tranne la prima
  500. # for x in client.playlistinfo()[1:]:
  501. # client.delete(1)
  502. # time.sleep(0.5)
  503. # #e rifa la playlist
  504. # for f in plt:
  505. # client.add(f[0])
  506. # time.sleep(0.5)
  507. print("------------------")
  508. playlist=client.playlistinfo()
  509. for f in plt[:len(playlist)-1]:
  510. i = plt.index(f) + 1
  511. #print(f[0] +" - "+ playlist[i]['file'])
  512. if f[0] != playlist[i]['file']:
  513. i = i - 1
  514. break
  515. else:
  516. print("Mantengo " + f[0])
  517. i = i + 1
  518. #print (i)
  519. for x in client.playlistinfo()[i:]:
  520. print("Cancello " + x['file'])
  521. client.delete(i)
  522. #e rifa la playlist
  523. for f in plt[i-1:]:
  524. print("Aggiungo " + f[0])
  525. client.add(f[0])
  526. #consumo la prima
  527. consume(plt[0][0])
  528. def trimaudio(fin, fout):
  529. from pydub import AudioSegment
  530. from pydub.silence import split_on_silence
  531. maxlen = 240
  532. tolerance = 15
  533. fade = 10
  534. audio = AudioSegment.from_file(fin, "m4a")
  535. #print(audio.duration_seconds)
  536. def match_target_amplitude(sound, target_dBFS):
  537. change_in_dBFS = target_dBFS - sound.dBFS
  538. return sound.apply_gain(change_in_dBFS)
  539. #trim
  540. audio = split_on_silence(audio, min_silence_len=3000, silence_thresh=-70.0, seek_step=100)[0]
  541. #print(audio.duration_seconds)
  542. #fade
  543. if (audio.duration_seconds > maxlen + tolerance):
  544. audio = audio[:maxlen*1000].fade_out(fade*1000)
  545. #print(audio.duration_seconds)
  546. #normalize
  547. audio = match_target_amplitude(audio, -20.0)
  548. audio.export(fout, format="mp4")
  549. if __name__ == '__main__':
  550. parser = argparse.ArgumentParser('Playlistalo')
  551. subparsers = parser.add_subparsers(dest='command')
  552. parser_add = subparsers.add_parser('add', help='Add song from file')
  553. parser_add.add_argument('file', help='Song file')
  554. parser_add.add_argument('-u', '--user', help='User that sends the file', default=None)
  555. parser_addurl = subparsers.add_parser('addurl', help='Add song from url')
  556. parser_addurl.add_argument('url', help='Song url')
  557. parser_addurl.add_argument('-u', '--user', help='User that sends the file', default=None)
  558. parser_addurl = subparsers.add_parser('list', help='List curent playlist')
  559. parser_addurl = subparsers.add_parser('mastodon', help='Run Mastodon bot')
  560. parser_addurl = subparsers.add_parser('telegram', help='Run Telegram bot')
  561. parser_addurl = subparsers.add_parser('playlocal', help='Play songs locally')
  562. parser_addurl = subparsers.add_parser('plaympd', help='Play songs using MPD')
  563. args = parser.parse_args()
  564. if args.command == 'add':
  565. print(add(args.file, args.user))
  566. elif args.command == 'addurl':
  567. print(addurl(args.url, args.user))
  568. elif args.command == 'list':
  569. pl = listtot()
  570. #print ('\n'.join([", ".join(x) for x in pl]))
  571. print ('\n'.join([x[0] for x in pl]))
  572. elif args.command == 'mastodon':
  573. mastodon_bot()
  574. elif args.command == 'telegram':
  575. telegram_bot()
  576. elif args.command == 'playlocal':
  577. playlocal()
  578. elif args.command == 'plaympd':
  579. plaympd()
  580. else:
  581. parser.print_help()