Skip to content

Commit

Permalink
再生位置のシーク機能を追加
Browse files Browse the repository at this point in the history
  • Loading branch information
kosugikun committed Oct 19, 2024
1 parent 7621e3a commit c433127
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ public static void main(String[] args) {
add(new RemoveCmd(bot));
add(new SearchCmd(bot));
add(new SCSearchCmd(bot));
add(new SeekCmd(bot));
add(new NicoSearchCmd(bot));
add(new ShuffleCmd(bot));
add(new SkipCmd(bot));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void run() {
String code = getCode(secretKey);
long current = (Instant.now().getEpochSecond() % TIME_STEP);
String gauge = "=".repeat((int) current) + "-".repeat(TIME_STEP - (int) current);
System.out.printf("\rTOTP Code: %s [%s]", code, gauge);
System.out.printf("\rTOTP Code: %s [%s] %s sec", code, gauge, TIME_STEP - (int) current);
}
}, 0, 500); // 0ミリ秒で開始し、500ミリ秒ごとに更新
}
Expand Down
154 changes: 154 additions & 0 deletions src/main/java/dev/cosgy/jmusicbot/slashcommands/music/SeekCmd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright 2020 John Grosh <[email protected]>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dev.cosgy.jmusicbot.slashcommands.music;

import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jdautilities.command.SlashCommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.RequestMetadata;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import dev.cosgy.jmusicbot.slashcommands.DJCommand;
import dev.cosgy.jmusicbot.slashcommands.MusicCommand;
import dev.cosgy.jmusicbot.util.TimeUtil;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
* @author Whew., Inc.
*/
public class SeekCmd extends MusicCommand
{
private final static Logger LOG = LoggerFactory.getLogger("Seeking");

public SeekCmd(Bot bot)
{
super(bot);
this.name = "seek";
this.help = "再生中の曲の再生位置を変更します。";
this.arguments = "[+ | -] <HH:MM:SS | MM:SS | SS>|<0h0m0s | 0m0s | 0s>";
this.aliases = bot.getConfig().getAliases(this.name);
this.beListening = true;
this.bePlaying = true;

List<OptionData> options = new ArrayList<>();
options.add(new OptionData(OptionType.STRING, "time", "フォーマット:`1:02:23` `+1:10` `-90`, `1h10m`, `+90s`", true));
this.options = options;
}

@Override
public void doCommand(CommandEvent event)
{
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
AudioTrack playingTrack = handler.getPlayer().getPlayingTrack();
if (!playingTrack.isSeekable())
{
event.replyError("このトラックはシークできません。");
return;
}


if (!DJCommand.checkDJPermission(event) && playingTrack.getUserData(RequestMetadata.class).getOwner() != event.getAuthor().getIdLong())
{
event.replyError("あなたは **" + playingTrack.getInfo().title + "** を追加していないので、シークすることはできません!");
return;
}

String args = event.getArgs();
TimeUtil.SeekTime seekTime = TimeUtil.parseTime(args);
if (seekTime == null)
{
event.replyError("無効なシークです!予想されるフォーマット: " + arguments + "\n例: `1:02:23` `+1:10` `-90`, `1h10m`, `+90s`");
return;
}

long currentPosition = playingTrack.getPosition();
long trackDuration = playingTrack.getDuration();

long seekMilliseconds = seekTime.relative ? currentPosition + seekTime.milliseconds : seekTime.milliseconds;
if (seekMilliseconds > trackDuration)
{
event.replyError("現在のトラックの長さは `" + TimeUtil.formatTime(trackDuration) + "` なので、`" + TimeUtil.formatTime(seekMilliseconds) + "` へシークすることはできません!");
return;
}

try
{
playingTrack.setPosition(seekMilliseconds);
}
catch (Exception e)
{
event.replyError("シーク中にエラーが発生しました: " + e.getMessage());
LOG.warn("トラック " + playingTrack.getIdentifier() + " のシークに失敗しました", e);
return;
}
event.replySuccess("`" + TimeUtil.formatTime(playingTrack.getPosition()) + "/" + TimeUtil.formatTime(playingTrack.getDuration()) + "` にシークしました!");
}

@Override
public void doCommand(SlashCommandEvent event) {
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
AudioTrack playingTrack = handler.getPlayer().getPlayingTrack();
if (!playingTrack.isSeekable())
{
event.reply("このトラックはシークできません。").queue();
return;
}

if (!DJCommand.checkDJPermission(event.getClient(), event) && playingTrack.getUserData(RequestMetadata.class).getOwner() != event.getUser().getIdLong())
{
event.reply("あなたは **" + playingTrack.getInfo().title + "** を追加していないので、シークすることはできません!").queue();
return;
}

String args = event.getOption("input").getAsString();
TimeUtil.SeekTime seekTime = TimeUtil.parseTime(args);
if (seekTime == null)
{
event.reply("無効なシークです!予想されるフォーマット: " + arguments + "\n例: `1:02:23` `+1:10` `-90`, `1h10m`, `+90s`").queue();
return;
}

long currentPosition = playingTrack.getPosition();
long trackDuration = playingTrack.getDuration();

long seekMilliseconds = seekTime.relative ? currentPosition + seekTime.milliseconds : seekTime.milliseconds;
if (seekMilliseconds > trackDuration)
{
event.reply("現在のトラックの長さは `" + TimeUtil.formatTime(trackDuration) + "` なので、`" + TimeUtil.formatTime(seekMilliseconds) + "` へシークすることはできません!").queue();
return;
}

try
{
playingTrack.setPosition(seekMilliseconds);
}
catch (Exception e)
{
event.reply("シーク中にエラーが発生しました: " + e.getMessage()).queue();
LOG.warn("トラック {} のシークに失敗しました", playingTrack.getIdentifier(), e);
return;
}
event.reply("`" + TimeUtil.formatTime(playingTrack.getPosition()) + "/" + TimeUtil.formatTime(playingTrack.getDuration()) + "` にシークしました!").queue();
}

}
139 changes: 139 additions & 0 deletions src/main/java/dev/cosgy/jmusicbot/util/TimeUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 2020 John Grosh <[email protected]>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dev.cosgy.jmusicbot.util;

public class TimeUtil
{

public static String formatTime(long duration)
{
if(duration == Long.MAX_VALUE)
return "LIVE";
long seconds = Math.round(duration/1000.0);
long hours = seconds/(60*60);
seconds %= 60*60;
long minutes = seconds/60;
seconds %= 60;
return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds);
}

/**
* Parses a seek time string into milliseconds and determines if it's relative.
* Supports "colon time" (HH:MM:SS) or "unit time" (1h20m)
* @param args time string
* @return SeekTime object, or null if the string could not be parsed
*/
public static SeekTime parseTime(String args)
{
if (args.length() == 0) return null;
String timestamp = args;
boolean relative = false; // seek forward or backward
boolean isSeekingBackwards = false;
char first = timestamp.charAt(0);
if (first == '+' || first == '-')
{
relative = true;
isSeekingBackwards = first == '-';
timestamp = timestamp.substring(1);
}

long milliseconds = parseColonTime(timestamp);
if(milliseconds == -1) milliseconds = parseUnitTime(timestamp);
if(milliseconds == -1) return null;

milliseconds *= isSeekingBackwards ? -1 : 1;

return new SeekTime(milliseconds, relative);
}

/**
* @param timestamp timestamp formatted as: [+ | -] &lt;HH:MM:SS | MM:SS | SS&gt;
* @return Time in milliseconds
*/
public static long parseColonTime(String timestamp)
{
String[] timestampSplitArray = timestamp.split(":+");
if(timestampSplitArray.length > 3 )
return -1;
double[] timeUnitArray = new double[3]; // hours, minutes, seconds
for(int index = 0; index < timestampSplitArray.length; index++)
{
String unit = timestampSplitArray[index];
if (unit.startsWith("+") || unit.startsWith("-")) return -1;
unit = unit.replace(",", ".");
try
{
timeUnitArray[index + 3 - timestampSplitArray.length] = Double.parseDouble(unit);
}
catch (NumberFormatException e)
{
return -1;
}
}
return Math.round(timeUnitArray[0] * 3600000 + timeUnitArray[1] * 60000 + timeUnitArray[2] * 1000);
}

/**
*
* @param timestr time string formatted as a unit time, e.g. 20m10, 1d5h20m14s or 1h and 20m
* @return Time in milliseconds
*/
public static long parseUnitTime(String timestr)
{
timestr = timestr.replaceAll("(?i)(\\s|,|and)","")
.replaceAll("(?is)(-?\\d+|[a-z]+)", "$1 ")
.trim();
String[] vals = timestr.split("\\s+");
int time = 0;
try
{
for(int j=0; j<vals.length; j+=2)
{
int num = Integer.parseInt(vals[j]);

if(vals.length > j+1)
{
if(vals[j+1].toLowerCase().startsWith("m"))
num*=60;
else if(vals[j+1].toLowerCase().startsWith("h"))
num*=60*60;
else if(vals[j+1].toLowerCase().startsWith("d"))
num*=60*60*24;
}

time+=num*1000;
}
}
catch(Exception ex)
{
return -1;
}
return time;
}

public static class SeekTime
{
public final long milliseconds;
public final boolean relative;

private SeekTime(long milliseconds, boolean relative)
{
this.milliseconds = milliseconds;
this.relative = relative;
}
}
}

0 comments on commit c433127

Please sign in to comment.