playlistalo.py 22 KB

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