playlistalo.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. #!/usr/bin/env python3
  2. #Playlistalo - simpatico script che legge le cartelle e genera la playlist
  3. import youtube_dl
  4. import shutil
  5. import sys
  6. import re
  7. import os
  8. import validators
  9. from glob import glob
  10. import json
  11. import time
  12. import subprocess
  13. import random
  14. import configparser
  15. from musicpd import MPDClient
  16. from telegram.ext import Updater, MessageHandler, Filters
  17. from mastodon import Mastodon, StreamListener
  18. scriptpath = os.path.dirname(os.path.realpath(__file__))
  19. #Crea le cartelle
  20. if not os.path.exists("playlist"):
  21. os.makedirs("playlist")
  22. if not os.path.exists("cache"):
  23. os.makedirs("cache")
  24. if not os.path.exists("fallback"):
  25. os.makedirs("fallback")
  26. if not os.path.exists("archive"):
  27. os.makedirs("archive")
  28. #Scrivi la prima configurazione
  29. configfile = 'playlistalo.conf'
  30. if not os.path.exists(configfile):
  31. config = configparser.ConfigParser()
  32. config['playlistalo'] = {'ShuffleUsers': False, 'ShuffleSongs': False, 'ShuffleFallback': False, 'Telegram_token': "", 'Mastodon_token': "", 'Mastodon_url': ""}
  33. with open(configfile, 'w') as f:
  34. config.write(f)
  35. #Leggi la configurazione
  36. config = configparser.ConfigParser()
  37. config.read(configfile)
  38. playlistaloconf = config['playlistalo']
  39. SHUFFLEUSERS = playlistaloconf.getboolean('ShuffleUsers')
  40. SHUFFLESONGS = playlistaloconf.getboolean('ShuffleSongs')
  41. SHUFFLEFALLBACK = playlistaloconf.getboolean('ShuffleFallback')
  42. TELEGRAM_TOKEN = playlistaloconf.get('Telegram_token')
  43. MASTODON_TOKEN = playlistaloconf.get('Mastodon_token')
  44. MASTODON_URL = playlistaloconf.get('Mastodon_url')
  45. def add(url, user = "-unknown-"):
  46. #print ('--- Inizio ---')
  47. ydl_opts = {
  48. 'format': 'bestaudio[ext=m4a]',
  49. 'outtmpl': 'cache/%(id)s.m4a',
  50. 'noplaylist': True,
  51. 'quiet': True,
  52. }
  53. url = url.strip()
  54. print ("url: " + url)
  55. print ("user: " + user)
  56. if not validators.url(url):
  57. print ('--- URL malformato ---')
  58. return ("Err: url non valido")
  59. with youtube_dl.YoutubeDL(ydl_opts) as ydl:
  60. try:
  61. meta = ydl.extract_info(url, download = False)
  62. except youtube_dl.DownloadError as detail:
  63. print ('--- Errore video non disponibile ---')
  64. print(str(detail))
  65. return ("Err: " + str(detail))
  66. id = meta.get('id').strip()
  67. title = __normalizetext(meta.get('title'))
  68. print ('id: %s' %(id))
  69. print ('title: %s' %(title))
  70. #scrivo il json
  71. with open(os.path.join("cache", id + ".json"), 'w') as outfile:
  72. json.dump(meta, outfile, indent=4)
  73. #ho letto le info, ora controllo se il file esiste altrimenti lo scarico
  74. #miglioria: controllare se upload_date e' uguale a quella del json gia' esistente
  75. filetemp = os.path.join("cache", id + ".m4a")
  76. if not glob(filetemp):
  77. print ('--- Scarico ---')
  78. ydl.download([url]) #non ho capito perche' ma senza [] fa un carattere per volta
  79. if not os.path.isfile(filetemp):
  80. return("Err: file non scaricato")
  81. #se il file esiste gia' in playlist salto (potrebbe esserci, anche rinominato)
  82. if glob("playlist/**/*|" + id + ".*"):
  83. print ('--- File già presente ---')
  84. return ("Err: %s [%s] già presente" %(title, id))
  85. if not os.path.exists("playlist/" + user):
  86. os.makedirs("playlist/" + user)
  87. #qui compone il nome del file
  88. if SHUFFLESONGS:
  89. fileout = str(random.randrange(10**6)).zfill(14) + "|" + title + "|" + id + ".m4a"
  90. else:
  91. fileout = time.strftime("%Y%m%d%H%M%S") + "|" + title + "|" + id + ".m4a"
  92. fileout = os.path.join("playlist/" + user, fileout)
  93. print ('--- Converto ---')
  94. print (fileout)
  95. subprocess.call([scriptpath + "/trimaudio.sh", filetemp, fileout])
  96. if not os.path.isfile(fileout):
  97. return("Err: file non convertito")
  98. #cerca la posizione del pezzo appena inserito
  99. pos = getposition(fileout)
  100. print ('--- Fine ---')
  101. print ("")
  102. return ("OK: %s [%s] aggiunto alla playlist in posizione #%s" %(title, id, pos))
  103. def __normalizetext(s):
  104. if s is None:
  105. return None
  106. else:
  107. s = re.sub(r'[\\|/|:|*|?|"|<|>|\|]',r'',s)
  108. s = " ".join(s.split())
  109. return s
  110. def listplaylist():
  111. pl = []
  112. pl2 = []
  113. for udir in sorted(glob("playlist/*/")):
  114. #print (udir)
  115. user = os.path.basename(os.path.dirname(udir))
  116. #cerca il file last
  117. last = ""
  118. if os.path.exists(udir + "/last"):
  119. f = open(udir + "/last", "r")
  120. last = f.readline().rstrip()
  121. else:
  122. last = os.path.basename(sorted(glob(udir + "/*.m4a"))[0]).split("|")[0]
  123. #print ("LAST: " + last)
  124. #leggi i file nella cartella
  125. files = sorted(glob(udir + "/*.m4a"))
  126. seq = 0
  127. for file in files:
  128. bn = os.path.splitext(os.path.basename(file))[0]
  129. #print ("BASENAME: " + bn)
  130. seq = seq + 1
  131. dat = bn.split("|")[0]
  132. nam = bn.split("|")[1]
  133. cod = bn.split("|")[2]
  134. key = "-".join([str(seq).zfill(5), last, dat])
  135. #print ("KEY: " + key)
  136. plsong = [key, file, user, nam, cod]
  137. pl.append(plsong)
  138. pl.sort()
  139. #rimuove la prima colonna, che serve solo per l'ordinamento
  140. pl2 = [x[1:] for x in pl]
  141. #print (pl)
  142. #print ('\n'.join([", ".join(x) for x in pl]))
  143. #print ('\n'.join([x[0] for x in pl]))
  144. return pl2
  145. def listfallback():
  146. pl = []
  147. pl2 = []
  148. #leggi i file nella cartella
  149. files = sorted(glob("fallback/*.m4a"))
  150. seq = 0
  151. for file in files:
  152. bn = os.path.splitext(os.path.basename(file))[0]
  153. seq = seq + 1
  154. dat = bn.split("|")[0]
  155. nam = bn.split("|")[1]
  156. cod = bn.split("|")[2]
  157. key = "-".join([str(seq).zfill(5), dat])
  158. plsong = [key, file, "fallback", nam, cod]
  159. pl.append(plsong)
  160. pl.sort()
  161. #rimuove la prima colonna, che serve solo per l'ordinamento
  162. pl2 = [x[1:] for x in pl]
  163. return pl2
  164. def playsingle():
  165. pl = listplaylist()
  166. #print ('\n'.join([x[0] for x in pl]))
  167. if pl:
  168. firstsong = pl[0][0]
  169. print (firstsong)
  170. #qui fa play
  171. subprocess.call(["mplayer", "-nolirc", "-msglevel", "all=0:statusline=5", firstsong])
  172. #alla fine consuma
  173. os.rename(firstsong, "archive/" + os.path.basename(firstsong))
  174. #se non ci sono + file cancella la cartella
  175. if not glob(os.path.dirname(firstsong) + "/*.m4a"):
  176. shutil.rmtree(os.path.dirname(firstsong))
  177. else:
  178. with open(os.path.dirname(firstsong) + "/last", "w") as f:
  179. f.write(time.strftime("%Y%m%d%H%M%S"))
  180. else:
  181. #usa il fallback
  182. #eventualmente aggiungere file di avviso con istruzioni veloci
  183. plf = listfallback()
  184. #print ('\n'.join([x[0] for x in plf]))
  185. if plf:
  186. firstsong = plf[0][0]
  187. print (firstsong)
  188. #qui fa play
  189. subprocess.call(["mplayer", "-nolirc", "-msglevel", "all=0:statusline=5", firstsong])
  190. #alla fine consuma
  191. fname = time.strftime("%Y%m%d%H%M%S") + "|" + "|".join(os.path.basename(firstsong).split("|")[1:])
  192. fname = os.path.dirname(firstsong) + "/" + fname
  193. os.rename(firstsong, fname)
  194. def playloop():
  195. while True:
  196. playsingle()
  197. def clean():
  198. #cancella tutto dalla playlist
  199. shutil.rmtree("playlist")
  200. os.makedirs("playlist")
  201. def shuffleusers():
  202. #scrivere un numero casuale dentro a tutti i file last
  203. for udir in sorted(glob("playlist/*/")):
  204. #print (udir)
  205. with open(udir + "/last", "w") as f:
  206. f.write(str(random.randrange(10**6)).zfill(14))
  207. def shufflefallback():
  208. #rinominare con un numero casuale i file in fallback
  209. files = sorted(glob("fallback/*.m4a"))
  210. for file in files:
  211. fname = str(random.randrange(10**6)).zfill(14) + "|" + "|".join(os.path.basename(file).split("|")[1:])
  212. fname = os.path.dirname(file) + "/" + fname
  213. os.rename(file, fname)
  214. def getposition(file):
  215. pl = listplaylist()
  216. try:
  217. return([x[0] for x in pl].index(file) + 1)
  218. except:
  219. pass
  220. def telegram_msg_parser(bot, update):
  221. print("Messaggio ricevuto")
  222. msg = update.message.text
  223. id = str(update.message.from_user.id)
  224. username = update.message.from_user.username
  225. urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', msg)
  226. user = "t_" + "-".join([i for i in [id, username] if i])
  227. #print (urls)
  228. #print (user)
  229. if not urls:
  230. update.message.reply_text("Non ho trovato indirizzi validi...")
  231. return()
  232. update.message.reply_text("Messaggio ricevuto. Elaboro...")
  233. for url in urls:
  234. #update.message.reply_text("Scarico %s" %(url))
  235. # start the download
  236. dl = add(url, user)
  237. update.message.reply_text(dl)
  238. def telegram_bot():
  239. print ("Bot avviato")
  240. # Create the EventHandler and pass it your bot's token.
  241. updater = Updater(TELEGRAM_TOKEN)
  242. # Get the dispatcher to register handlers
  243. dp = updater.dispatcher
  244. # parse message
  245. dp.add_handler(MessageHandler(Filters.text, telegram_msg_parser))
  246. # Start the Bot
  247. updater.start_polling()
  248. # Run the bot until you press Ctrl-C
  249. updater.idle()
  250. class MastodonListener(StreamListener):
  251. # andiamo a definire il metodo __init__, che prende una istanza di Mastodon come parametro opzionale e lo setta nella prop. self.mastodon
  252. def __init__(self, mastodonInstance=None):
  253. self.mastodon = mastodonInstance
  254. def on_notification(self, notification):
  255. print("Messaggio ricevuto")
  256. #try:
  257. msg = notification["status"]["content"]
  258. id = str(notification["account"]["id"])
  259. username = notification["account"]["acct"]
  260. #except KeyError:
  261. # return
  262. msg = msg.replace("<br>", "\n")
  263. msg = re.compile(r'<.*?>').sub('', msg)
  264. urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', msg)
  265. user = "m_" + "-".join([i for i in [id, username] if i])
  266. #print (urls)
  267. #print (user)
  268. #visibility = notification['status']['visibility']
  269. statusid = notification['status']['id']
  270. if not urls:
  271. self.mastodon.status_post("@" + username + " " + "Non ho trovato indirizzi validi...", in_reply_to_id = statusid, visibility="direct")
  272. return()
  273. self.mastodon.status_post("@" + username + " " + "Messaggio ricevuto. Elaboro...", in_reply_to_id = statusid, visibility="direct")
  274. for url in urls:
  275. # start the download
  276. dl = add(url, user)
  277. self.mastodon.status_post("@" + username + " " + dl, in_reply_to_id = statusid, visibility="direct")
  278. def mastodon_bot():
  279. print ("Bot avviato")
  280. mastodon = Mastodon(access_token = MASTODON_TOKEN, api_base_url = MASTODON_URL)
  281. listener = MastodonListener(mastodon)
  282. mastodon.stream_user(listener)
  283. if __name__ == '__main__':
  284. print ("This is a package, use other commands to run it")